├── .gitignore ├── Readme.md ├── data-provisioning ├── pom.xml └── src │ ├── conf │ ├── config-docker-machine.json │ └── config.json │ └── main │ └── java │ └── io │ └── vertx │ └── workshop │ └── data │ └── provisioning │ └── MapMeProvisioning.java ├── data-storage-client ├── pom.xml └── src │ └── main │ └── resources │ └── Client.groovy ├── data-storage-service ├── pom.xml └── src │ ├── conf │ ├── config-docker-machine.json │ └── config.json │ └── main │ ├── asciidoc │ └── dataobjects.adoc │ ├── generated │ └── io │ │ └── vertx │ │ └── workshop │ │ └── data │ │ ├── DataStorageServiceVertxEBProxy.java │ │ ├── DataStorageServiceVertxProxyHandler.java │ │ └── PlaceConverter.java │ ├── groovy │ └── io │ │ └── vertx │ │ └── workshop │ │ └── groovy │ │ └── data │ │ └── DataStorageService.groovy │ ├── java │ └── io │ │ └── vertx │ │ └── workshop │ │ └── data │ │ ├── DataStorageService.java │ │ ├── Place.java │ │ ├── impl │ │ ├── DataStorageServiceImpl.java │ │ └── DataStorageVerticle.java │ │ └── package-info.java │ └── resources │ └── vertx-microservice-workshop-js │ ├── data_storage_service-proxy.js │ └── data_storage_service.js ├── etc ├── cluster.xml └── vertx-default-jul-logging-graylog.properties ├── eventbus-to-hawkular-bridge ├── pom.xml └── src │ ├── conf │ ├── config-docker-machine.json │ └── config.json │ └── main │ └── java │ └── io │ └── vertx │ └── workshop │ └── hawkular │ └── HawkularBridge.java ├── frontend ├── Readme.md ├── pom.xml └── src │ └── main │ └── resources │ ├── server.js │ └── webroot │ ├── css │ └── app.css │ ├── index.html │ └── js │ ├── app.js │ ├── controllers.js │ └── vendor │ └── vertx-eventbus.js ├── map-render-service ├── Readme.md ├── openshift │ ├── .openshift │ │ ├── README.md │ │ ├── action_hooks │ │ │ └── README.md │ │ └── markers │ │ │ └── README.md │ ├── application │ │ └── README.md │ └── configuration │ │ └── vertx.env ├── pom.xml └── src │ └── main │ ├── java │ └── io │ │ └── vertx │ │ └── workshop │ │ └── map │ │ ├── BoundingBox.java │ │ ├── Coordinate.java │ │ ├── MapException.java │ │ ├── index │ │ └── QTree.java │ │ ├── mercator │ │ └── Mercator.java │ │ ├── render │ │ ├── MapRenderVerticle.java │ │ ├── WebMapRenderVerticle.java │ │ └── backend │ │ │ ├── MapSurface.java │ │ │ └── Renderer.java │ │ ├── rules │ │ ├── Draw.java │ │ ├── Rule.java │ │ ├── RuleSet.java │ │ └── osm │ │ │ └── OSMRules.java │ │ └── source │ │ ├── MapSource.java │ │ ├── Member.java │ │ ├── Node.java │ │ ├── Relation.java │ │ ├── Way.java │ │ └── osm │ │ └── OSMReader.java │ └── resources │ ├── antwerpen.osm.idx.gz │ ├── pattern │ ├── landuse-cemetery-christian.png │ ├── landuse-cemetery.png │ ├── landuse-vineyard.png │ ├── military.png │ ├── wood-deciduous.png │ └── wood-mixed.png │ └── rule.xml ├── map-server-proxy ├── pom.xml └── src │ └── main │ ├── docker │ └── assembly.xml │ └── java │ └── io │ └── vertx │ └── workshop │ └── map │ └── server │ └── MapServerVerticle.java ├── pom.xml ├── recommendation-service ├── pom.xml └── src │ ├── conf │ ├── config-docker-machine.json │ └── config.json │ └── main │ ├── generated │ └── io │ │ └── vertx │ │ └── workshop │ │ └── recommendation │ │ ├── RecommendationServiceVertxEBProxy.java │ │ └── RecommendationServiceVertxProxyHandler.java │ ├── groovy │ └── io │ │ └── vertx │ │ └── workshop │ │ └── groovy │ │ └── recommendation │ │ └── RecommendationService.groovy │ ├── java │ └── io │ │ └── vertx │ │ └── workshop │ │ └── recommendation │ │ ├── RecommendationService.java │ │ ├── impl │ │ ├── RecommendationServiceImpl.java │ │ └── RecommendationVerticle.java │ │ └── package-info.java │ └── resources │ └── vertx-microservice-workshop-js │ ├── recommendation_service-proxy.js │ └── recommendation_service.js └── scripts ├── launch-grafana.sh ├── launch-hawkular.sh ├── launch-mongo.sh ├── launch-redis.sh ├── provision-docker-registry.sh ├── stop-mongo.sh └── stop-redis.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | pom.xml.next 7 | release.properties 8 | dependency-reduced-pom.xml 9 | buildNumber.properties 10 | 11 | # Eclipse 12 | .settings 13 | .classpath 14 | .project 15 | 16 | # IntelliJ 17 | .idea 18 | *.iml 19 | 20 | # vert.x 21 | .vertx 22 | 23 | # misc binaries 24 | map-render-service-1.0-SNAPSHOT-fat.jar 25 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # vert.x micro-service workshop 2 | 3 | In this workshop, you are going to develop a micro-service application called _vert2go_. This workshop covers: 4 | 5 | * how to build micro-services with vert.x 6 | * how you can use the event bus to bind your micro-services 7 | * how to consume a REST API 8 | * how to build an application using several persistence technologies 9 | * how to provide a REST API, and build proxies 10 | * how to deploy some micro-service to Openshift 11 | * how to centralize your logs 12 | * how to deploy a vert.x micro-service in a docker container 13 | * how to monitor your application 14 | 15 | The _vert2go_ application is a recommendation application where users can rates the place they like. 16 | 17 | The slides are available on: http://devoxx0workshop0slides-vertxdemos.rhcloud.com/#/ 18 | 19 | **Prerequisites:** 20 | 21 | * Java 8 (JDK) 22 | * Git 23 | * Apache Maven 24 | * Docker (or docker-machine) 25 | * An IDE 26 | * RoboMongo (optional) 27 | 28 | 29 | ## Using the provided virtual box image 30 | 31 | We provide a virtual box image with everything you need on it. 32 | 33 | 1. Install Oracle virtual box from https://www.virtualbox.org/wiki/Downloads. Installation details are on https://www.virtualbox.org/manual/ch02.html 34 | 2. Retrieve the virtual box image (`vertx-workshop.ova`). 35 | 3. Open virtual box and import the _appliance_ (File -> Import Appliance) 36 | 4. Select the `vertx-workshop.ova` file and click on import on the next screen 37 | 5. Wait.... 38 | 6. Start the virtual machine by selecting it and then clic on the _Start_ button (green arrow) 39 | 40 | ## Using docker machine 41 | 42 | Docker runs natively on Linux. Because the Docker daemon uses Linux-specific kernel features, you can’t run Docker 43 | natively in OS X or Windows. Instead, you must use docker-machine to create and attach to a virtual machine (VM). This 44 | machine is a Linux VM that hosts Docker for you on your Mac or Windows. If you are on Mac OS X or Windows, you can use 45 | docker via docker machine (https://docs.docker.com/machine/). 46 | 47 | 1. Install docker and docker-machine from https://www.docker.com/docker-toolbox. Installation instructions are there: http://docs.docker.com/mac/step_one/ 48 | 2. Once done run `docker run hello-world` to verify your installation 49 | 50 | The installation process will create a VM that has a minimal linux to run docker. 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /data-provisioning/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | io.vertx.workshop 9 | vertx-microservice-workshop 10 | 1.0-SNAPSHOT 11 | 12 | 13 | data-provisioning 14 | Data Provisioner 15 | 16 | 17 | io.vertx.workshop.data.provisioning.MapMeProvisioning 18 | 19 | 20 | 21 | 22 | ${project.groupId} 23 | data-storage-service 24 | ${project.version} 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-shade-plugin 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /data-provisioning/src/conf/config-docker-machine.json: -------------------------------------------------------------------------------- 1 | { 2 | "mongoURL": "mongodb://192.168.99.100:27017" 3 | } -------------------------------------------------------------------------------- /data-provisioning/src/conf/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mongoURL": "mongodb://localhost:27017" 3 | } -------------------------------------------------------------------------------- /data-provisioning/src/main/java/io/vertx/workshop/data/provisioning/MapMeProvisioning.java: -------------------------------------------------------------------------------- 1 | package io.vertx.workshop.data.provisioning; 2 | 3 | import io.vertx.core.AbstractVerticle; 4 | import io.vertx.core.Handler; 5 | import io.vertx.core.http.HttpClient; 6 | import io.vertx.core.json.JsonArray; 7 | import io.vertx.core.json.JsonObject; 8 | import io.vertx.ext.mongo.MongoClient; 9 | import io.vertx.workshop.data.Place; 10 | 11 | import java.util.Collection; 12 | import java.util.Collections; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * @author Clement Escoffier 18 | */ 19 | public class MapMeProvisioning extends AbstractVerticle { 20 | 21 | private String mongoURL; 22 | 23 | @Override 24 | public void start() { 25 | mongoURL = config().getString("mongoURL"); 26 | provision(); 27 | } 28 | 29 | 30 | private void provision() { 31 | HttpClient client = vertx.createHttpClient(); 32 | //URL for devoxx. 33 | client.getNow(80, "mapme.com", "/api/map/910f1efe-d403-481e-a87e-bd8c9df7a131/places", response -> 34 | response.bodyHandler(body -> { 35 | /** 36 | * TODO to implement (2 lines) 37 | */ 38 | } 39 | ) 40 | ); 41 | } 42 | 43 | private void populateDatabase(Collection places) { 44 | MongoClient mongo = MongoClient.createShared(vertx, 45 | new JsonObject().put("db_name", "places").put("connection_string", mongoURL), 46 | "places"); 47 | 48 | places.stream().forEach(place -> 49 | mongo.insert("places", place.toJson(), result -> { 50 | if (result.failed()) { 51 | System.err.println("I was not able to insert '" + place.getName() + "' : " + result.cause().getMessage()); 52 | } else { 53 | System.out.println("Place '" + place.getName() + "' inserted"); 54 | } 55 | })); 56 | } 57 | 58 | private void extractPlaces(JsonObject json, Handler> resultHandler) { 59 | Map placesByName = new HashMap<>(); 60 | JsonObject categories = json.getJsonObject("categories"); 61 | categories.stream().forEach(entry -> { 62 | String category = entry.getKey(); 63 | JsonObject cat = (JsonObject) entry.getValue(); 64 | JsonObject tags = cat.getJsonObject("tags"); 65 | tags.stream().forEach(tagEntry -> { 66 | String tag = tagEntry.getKey(); 67 | JsonArray places = ((JsonObject) tagEntry.getValue()).getJsonArray("places"); 68 | places.stream().forEach(p -> { 69 | Place place = createPlaceFromMapMe((JsonObject) p, tag, category); 70 | if (placesByName.containsKey(place.getName())) { 71 | placesByName.get(place.getName()).addTag(tag); 72 | } else { 73 | placesByName.put(place.getName(), place); 74 | } 75 | }); 76 | }); 77 | }); 78 | 79 | resultHandler.handle(placesByName.values()); 80 | } 81 | 82 | private Place createPlaceFromMapMe(JsonObject json, String tag, String category) { 83 | Place place = new Place(); 84 | return place 85 | .setAddress(json.getString("addressDisplay", "")) 86 | .setCategory(category) 87 | .setDescription(json.getString("description", "")) 88 | .setLatitude(json.getDouble("lat", -1.0)) 89 | .setLongitude(json.getDouble("lon", -1.0)) 90 | .setName(json.getString("companyName")) 91 | .setTags(Collections.singletonList(tag)); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /data-storage-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | io.vertx.workshop 9 | vertx-microservice-workshop 10 | 1.0-SNAPSHOT 11 | 12 | 13 | data-storage-client 14 | Data Storage Client 15 | 16 | 17 | Client.groovy 18 | 19 | 20 | 21 | 22 | ${project.groupId} 23 | data-storage-service 24 | ${project.version} 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-shade-plugin 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /data-storage-client/src/main/resources/Client.groovy: -------------------------------------------------------------------------------- 1 | import io.vertx.workshop.groovy.data.* 2 | 3 | //TODO Change address to the address you chose. 4 | def service = DataStorageService.createProxy(vertx, "devoxx.places") 5 | 6 | service.getAllPlaces({ 7 | /** 8 | * TODO to implement 9 | */ 10 | result -> 11 | }) 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /data-storage-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | io.vertx.workshop 9 | vertx-microservice-workshop 10 | 1.0-SNAPSHOT 11 | 12 | 13 | data-storage-service 14 | Data Storage Service 15 | 16 | 17 | io.vertx.workshop.data.impl.DataStorageVerticle 18 | 19 | 20 | 21 | 22 | io.vertx 23 | vertx-mongo-client 24 | 25 | 26 | io.vertx 27 | vertx-service-proxy 28 | 29 | 30 | 31 | io.vertx 32 | vertx-lang-groovy 33 | 34 | 35 | io.vertx 36 | vertx-lang-js 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-shade-plugin 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /data-storage-service/src/conf/config-docker-machine.json: -------------------------------------------------------------------------------- 1 | { 2 | "db_name" : "places", 3 | "connection_string": "mongodb://192.168.99.100:27017" 4 | } -------------------------------------------------------------------------------- /data-storage-service/src/conf/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "db_name" : "places", 3 | "connection_string": "mongodb://localhost:27017" 4 | } -------------------------------------------------------------------------------- /data-storage-service/src/main/asciidoc/dataobjects.adoc: -------------------------------------------------------------------------------- 1 | = Cheatsheets 2 | 3 | [[Place]] 4 | == Place 5 | 6 | ++++ 7 | The place structure. 8 | 9 | It's a data object that can be serialized to JSON (and built from JSON). Data objects can be used in event bus 10 | proxies. 11 | ++++ 12 | ''' 13 | 14 | [cols=">25%,^25%,50%"] 15 | [frame="topbot"] 16 | |=== 17 | ^|Name | Type ^| Description 18 | |[[address]]`address`|`String`|- 19 | |[[category]]`category`|`String`|- 20 | |[[description]]`description`|`String`|- 21 | |[[latitude]]`latitude`|`Number (double)`|- 22 | |[[longitude]]`longitude`|`Number (double)`|- 23 | |[[name]]`name`|`String`|- 24 | |[[tags]]`tags`|`Array of String`|- 25 | |=== 26 | 27 | -------------------------------------------------------------------------------- /data-storage-service/src/main/generated/io/vertx/workshop/data/DataStorageServiceVertxEBProxy.java: -------------------------------------------------------------------------------- 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 | package io.vertx.workshop.data; 18 | 19 | import io.vertx.workshop.data.DataStorageService; 20 | import io.vertx.core.eventbus.DeliveryOptions; 21 | import io.vertx.core.Vertx; 22 | import io.vertx.core.Future; 23 | import io.vertx.core.json.JsonObject; 24 | import io.vertx.core.json.JsonArray; 25 | import java.util.ArrayList; 26 | import java.util.HashSet; 27 | import java.util.List; 28 | import java.util.Map; 29 | import java.util.Set; 30 | import java.util.stream.Collectors; 31 | import io.vertx.serviceproxy.ProxyHelper; 32 | import io.vertx.workshop.data.DataStorageService; 33 | import java.util.List; 34 | import io.vertx.workshop.data.Place; 35 | import io.vertx.core.Vertx; 36 | import io.vertx.core.AsyncResult; 37 | import io.vertx.core.Handler; 38 | 39 | /* 40 | Generated Proxy code - DO NOT EDIT 41 | @author Roger the Robot 42 | */ 43 | public class DataStorageServiceVertxEBProxy implements DataStorageService { 44 | 45 | private Vertx _vertx; 46 | private String _address; 47 | private DeliveryOptions _options; 48 | private boolean closed; 49 | 50 | public DataStorageServiceVertxEBProxy(Vertx vertx, String address) { 51 | this(vertx, address, null); 52 | } 53 | 54 | public DataStorageServiceVertxEBProxy(Vertx vertx, String address, DeliveryOptions options) { 55 | this._vertx = vertx; 56 | this._address = address; 57 | this._options = options; 58 | } 59 | 60 | public void getAllPlaces(Handler>> resultHandler) { 61 | if (closed) { 62 | resultHandler.handle(Future.failedFuture(new IllegalStateException("Proxy is closed"))); 63 | return; 64 | } 65 | JsonObject _json = new JsonObject(); 66 | DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions(); 67 | _deliveryOptions.addHeader("action", "getAllPlaces"); 68 | _vertx.eventBus().send(_address, _json, _deliveryOptions, res -> { 69 | if (res.failed()) { 70 | resultHandler.handle(Future.failedFuture(res.cause())); 71 | } else { 72 | resultHandler.handle(Future.succeededFuture(res.result().body().stream().map(o -> o instanceof Map ? new Place(new JsonObject((Map) o)) : new Place((JsonObject) o)).collect(Collectors.toList()))); 73 | } 74 | }); 75 | } 76 | 77 | public void getPlacesForCategory(String category, Handler>> resultHandler) { 78 | if (closed) { 79 | resultHandler.handle(Future.failedFuture(new IllegalStateException("Proxy is closed"))); 80 | return; 81 | } 82 | JsonObject _json = new JsonObject(); 83 | _json.put("category", category); 84 | DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions(); 85 | _deliveryOptions.addHeader("action", "getPlacesForCategory"); 86 | _vertx.eventBus().send(_address, _json, _deliveryOptions, res -> { 87 | if (res.failed()) { 88 | resultHandler.handle(Future.failedFuture(res.cause())); 89 | } else { 90 | resultHandler.handle(Future.succeededFuture(res.result().body().stream().map(o -> o instanceof Map ? new Place(new JsonObject((Map) o)) : new Place((JsonObject) o)).collect(Collectors.toList()))); 91 | } 92 | }); 93 | } 94 | 95 | public void getPlacesForTag(String tag, Handler>> resultHandler) { 96 | if (closed) { 97 | resultHandler.handle(Future.failedFuture(new IllegalStateException("Proxy is closed"))); 98 | return; 99 | } 100 | JsonObject _json = new JsonObject(); 101 | _json.put("tag", tag); 102 | DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions(); 103 | _deliveryOptions.addHeader("action", "getPlacesForTag"); 104 | _vertx.eventBus().send(_address, _json, _deliveryOptions, res -> { 105 | if (res.failed()) { 106 | resultHandler.handle(Future.failedFuture(res.cause())); 107 | } else { 108 | resultHandler.handle(Future.succeededFuture(res.result().body().stream().map(o -> o instanceof Map ? new Place(new JsonObject((Map) o)) : new Place((JsonObject) o)).collect(Collectors.toList()))); 109 | } 110 | }); 111 | } 112 | 113 | public void addPlace(Place place, Handler> resultHandler) { 114 | if (closed) { 115 | resultHandler.handle(Future.failedFuture(new IllegalStateException("Proxy is closed"))); 116 | return; 117 | } 118 | JsonObject _json = new JsonObject(); 119 | _json.put("place", place == null ? null : place.toJson()); 120 | DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions(); 121 | _deliveryOptions.addHeader("action", "addPlace"); 122 | _vertx.eventBus().send(_address, _json, _deliveryOptions, res -> { 123 | if (res.failed()) { 124 | resultHandler.handle(Future.failedFuture(res.cause())); 125 | } else { 126 | resultHandler.handle(Future.succeededFuture(res.result().body())); 127 | } 128 | }); 129 | } 130 | 131 | 132 | private List convertToListChar(JsonArray arr) { 133 | List list = new ArrayList<>(); 134 | for (Object obj: arr) { 135 | Integer jobj = (Integer)obj; 136 | list.add((char)(int)jobj); 137 | } 138 | return list; 139 | } 140 | 141 | private Set convertToSetChar(JsonArray arr) { 142 | Set set = new HashSet<>(); 143 | for (Object obj: arr) { 144 | Integer jobj = (Integer)obj; 145 | set.add((char)(int)jobj); 146 | } 147 | return set; 148 | } 149 | 150 | private Map convertMap(Map map) { 151 | return (Map)map; 152 | } 153 | private List convertList(List list) { 154 | return (List)list; 155 | } 156 | private Set convertSet(List list) { 157 | return new HashSet((List)list); 158 | } 159 | } -------------------------------------------------------------------------------- /data-storage-service/src/main/generated/io/vertx/workshop/data/DataStorageServiceVertxProxyHandler.java: -------------------------------------------------------------------------------- 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 | package io.vertx.workshop.data; 18 | 19 | import io.vertx.workshop.data.DataStorageService; 20 | import io.vertx.core.Vertx; 21 | import io.vertx.core.Handler; 22 | import io.vertx.core.AsyncResult; 23 | import io.vertx.core.eventbus.EventBus; 24 | import io.vertx.core.eventbus.Message; 25 | import io.vertx.core.eventbus.MessageConsumer; 26 | import io.vertx.core.eventbus.DeliveryOptions; 27 | import io.vertx.core.eventbus.ReplyException; 28 | import io.vertx.core.json.JsonObject; 29 | import io.vertx.core.json.JsonArray; 30 | import java.util.Collection; 31 | import java.util.ArrayList; 32 | import java.util.HashSet; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.Set; 36 | import java.util.UUID; 37 | import java.util.stream.Collectors; 38 | import io.vertx.serviceproxy.ProxyHelper; 39 | import io.vertx.serviceproxy.ProxyHandler; 40 | import io.vertx.workshop.data.DataStorageService; 41 | import java.util.List; 42 | import io.vertx.workshop.data.Place; 43 | import io.vertx.core.Vertx; 44 | import io.vertx.core.AsyncResult; 45 | import io.vertx.core.Handler; 46 | 47 | /* 48 | Generated Proxy code - DO NOT EDIT 49 | @author Roger the Robot 50 | */ 51 | public class DataStorageServiceVertxProxyHandler extends ProxyHandler { 52 | 53 | public static final long DEFAULT_CONNECTION_TIMEOUT = 5 * 60; // 5 minutes 54 | 55 | private final Vertx vertx; 56 | private final DataStorageService service; 57 | private final long timerID; 58 | private long lastAccessed; 59 | private final long timeoutSeconds; 60 | 61 | public DataStorageServiceVertxProxyHandler(Vertx vertx, DataStorageService service) { 62 | this(vertx, service, DEFAULT_CONNECTION_TIMEOUT); 63 | } 64 | 65 | public DataStorageServiceVertxProxyHandler(Vertx vertx, DataStorageService service, long timeoutInSecond) { 66 | this(vertx, service, true, timeoutInSecond); 67 | } 68 | 69 | public DataStorageServiceVertxProxyHandler(Vertx vertx, DataStorageService service, boolean topLevel, long timeoutSeconds) { 70 | this.vertx = vertx; 71 | this.service = service; 72 | this.timeoutSeconds = timeoutSeconds; 73 | if (timeoutSeconds != -1 && !topLevel) { 74 | long period = timeoutSeconds * 1000 / 2; 75 | if (period > 10000) { 76 | period = 10000; 77 | } 78 | this.timerID = vertx.setPeriodic(period, this::checkTimedOut); 79 | } else { 80 | this.timerID = -1; 81 | } 82 | accessed(); 83 | } 84 | 85 | public MessageConsumer registerHandler(String address) { 86 | MessageConsumer consumer = vertx.eventBus().consumer(address).handler(this); 87 | this.setConsumer(consumer); 88 | return consumer; 89 | } 90 | 91 | private void checkTimedOut(long id) { 92 | long now = System.nanoTime(); 93 | if (now - lastAccessed > timeoutSeconds * 1000000000) { 94 | close(); 95 | } 96 | } 97 | 98 | @Override 99 | public void close() { 100 | if (timerID != -1) { 101 | vertx.cancelTimer(timerID); 102 | } 103 | super.close(); 104 | } 105 | 106 | private void accessed() { 107 | this.lastAccessed = System.nanoTime(); 108 | } 109 | 110 | public void handle(Message msg) { 111 | try { 112 | JsonObject json = msg.body(); 113 | String action = msg.headers().get("action"); 114 | if (action == null) { 115 | throw new IllegalStateException("action not specified"); 116 | } 117 | accessed(); 118 | switch (action) { 119 | 120 | case "getAllPlaces": { 121 | service.getAllPlaces(res -> { 122 | if (res.failed()) { 123 | msg.fail(-1, res.cause().getMessage()); 124 | } else { 125 | msg.reply(new JsonArray(res.result().stream().map(Place::toJson).collect(Collectors.toList()))); 126 | } 127 | }); 128 | break; 129 | } 130 | case "getPlacesForCategory": { 131 | service.getPlacesForCategory((java.lang.String)json.getValue("category"), res -> { 132 | if (res.failed()) { 133 | msg.fail(-1, res.cause().getMessage()); 134 | } else { 135 | msg.reply(new JsonArray(res.result().stream().map(Place::toJson).collect(Collectors.toList()))); 136 | } 137 | }); 138 | break; 139 | } 140 | case "getPlacesForTag": { 141 | service.getPlacesForTag((java.lang.String)json.getValue("tag"), res -> { 142 | if (res.failed()) { 143 | msg.fail(-1, res.cause().getMessage()); 144 | } else { 145 | msg.reply(new JsonArray(res.result().stream().map(Place::toJson).collect(Collectors.toList()))); 146 | } 147 | }); 148 | break; 149 | } 150 | case "addPlace": { 151 | service.addPlace(json.getJsonObject("place") == null ? null : new io.vertx.workshop.data.Place(json.getJsonObject("place")), createHandler(msg)); 152 | break; 153 | } 154 | default: { 155 | throw new IllegalStateException("Invalid action: " + action); 156 | } 157 | } 158 | } catch (Throwable t) { 159 | msg.fail(-1, t.getMessage()); 160 | throw t; 161 | } 162 | } 163 | 164 | private Handler> createHandler(Message msg) { 165 | return res -> { 166 | if (res.failed()) { 167 | msg.fail(-1, res.cause().getMessage()); 168 | } else { 169 | msg.reply(res.result()); 170 | } 171 | }; 172 | } 173 | 174 | private Handler>> createListHandler(Message msg) { 175 | return res -> { 176 | if (res.failed()) { 177 | msg.fail(-1, res.cause().getMessage()); 178 | } else { 179 | msg.reply(new JsonArray(res.result())); 180 | } 181 | }; 182 | } 183 | 184 | private Handler>> createSetHandler(Message msg) { 185 | return res -> { 186 | if (res.failed()) { 187 | msg.fail(-1, res.cause().getMessage()); 188 | } else { 189 | msg.reply(new JsonArray(new ArrayList<>(res.result()))); 190 | } 191 | }; 192 | } 193 | 194 | private Handler>> createListCharHandler(Message msg) { 195 | return res -> { 196 | if (res.failed()) { 197 | msg.fail(-1, res.cause().getMessage()); 198 | } else { 199 | JsonArray arr = new JsonArray(); 200 | for (Character chr: res.result()) { 201 | arr.add((int) chr); 202 | } 203 | msg.reply(arr); 204 | } 205 | }; 206 | } 207 | 208 | private Handler>> createSetCharHandler(Message msg) { 209 | return res -> { 210 | if (res.failed()) { 211 | msg.fail(-1, res.cause().getMessage()); 212 | } else { 213 | JsonArray arr = new JsonArray(); 214 | for (Character chr: res.result()) { 215 | arr.add((int) chr); 216 | } 217 | msg.reply(arr); 218 | } 219 | }; 220 | } 221 | 222 | private Map convertMap(Map map) { 223 | return (Map)map; 224 | } 225 | 226 | private List convertList(List list) { 227 | return (List)list; 228 | } 229 | 230 | private Set convertSet(List list) { 231 | return new HashSet((List)list); 232 | } 233 | } -------------------------------------------------------------------------------- /data-storage-service/src/main/generated/io/vertx/workshop/data/PlaceConverter.java: -------------------------------------------------------------------------------- 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 | package io.vertx.workshop.data; 18 | 19 | import io.vertx.core.json.JsonObject; 20 | import io.vertx.core.json.JsonArray; 21 | 22 | /** 23 | * Converter for {@link io.vertx.workshop.data.Place}. 24 | * 25 | * NOTE: This class has been automatically generated from the {@link io.vertx.workshop.data.Place} original class using Vert.x codegen. 26 | */ 27 | public class PlaceConverter { 28 | 29 | public static void fromJson(JsonObject json, Place obj) { 30 | if (json.getValue("address") instanceof String) { 31 | obj.setAddress((String)json.getValue("address")); 32 | } 33 | if (json.getValue("category") instanceof String) { 34 | obj.setCategory((String)json.getValue("category")); 35 | } 36 | if (json.getValue("description") instanceof String) { 37 | obj.setDescription((String)json.getValue("description")); 38 | } 39 | if (json.getValue("latitude") instanceof Number) { 40 | obj.setLatitude(((Number)json.getValue("latitude")).doubleValue()); 41 | } 42 | if (json.getValue("longitude") instanceof Number) { 43 | obj.setLongitude(((Number)json.getValue("longitude")).doubleValue()); 44 | } 45 | if (json.getValue("name") instanceof String) { 46 | obj.setName((String)json.getValue("name")); 47 | } 48 | if (json.getValue("tags") instanceof JsonArray) { 49 | json.getJsonArray("tags").forEach(item -> { 50 | if (item instanceof String) 51 | obj.addTag((String)item); 52 | }); 53 | } 54 | } 55 | 56 | public static void toJson(Place obj, JsonObject json) { 57 | if (obj.getAddress() != null) { 58 | json.put("address", obj.getAddress()); 59 | } 60 | if (obj.getCategory() != null) { 61 | json.put("category", obj.getCategory()); 62 | } 63 | if (obj.getDescription() != null) { 64 | json.put("description", obj.getDescription()); 65 | } 66 | json.put("latitude", obj.getLatitude()); 67 | json.put("longitude", obj.getLongitude()); 68 | if (obj.getName() != null) { 69 | json.put("name", obj.getName()); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /data-storage-service/src/main/groovy/io/vertx/workshop/groovy/data/DataStorageService.groovy: -------------------------------------------------------------------------------- 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 | package io.vertx.workshop.groovy.data; 18 | import groovy.transform.CompileStatic 19 | import io.vertx.lang.groovy.InternalHelper 20 | import io.vertx.core.json.JsonObject 21 | import java.util.List 22 | import io.vertx.workshop.data.Place 23 | import io.vertx.groovy.core.Vertx 24 | import io.vertx.core.AsyncResult 25 | import io.vertx.core.Handler 26 | /** 27 | * Service exposed on the event bus to provide access to 28 | * the stored Places. 29 | */ 30 | @CompileStatic 31 | public class DataStorageService { 32 | private final def io.vertx.workshop.data.DataStorageService delegate; 33 | public DataStorageService(Object delegate) { 34 | this.delegate = (io.vertx.workshop.data.DataStorageService) delegate; 35 | } 36 | public Object getDelegate() { 37 | return delegate; 38 | } 39 | /** 40 | * Method called to create a proxy (to consume the service). 41 | * @param vertx vert.x 42 | * @param address the address on the vent bus where the service is served. 43 | * @return the proxy on the {@link io.vertx.workshop.groovy.data.DataStorageService} 44 | */ 45 | public static DataStorageService createProxy(Vertx vertx, String address) { 46 | def ret= InternalHelper.safeCreate(io.vertx.workshop.data.DataStorageService.createProxy((io.vertx.core.Vertx)vertx.getDelegate(), address), io.vertx.workshop.groovy.data.DataStorageService.class); 47 | return ret; 48 | } 49 | /** 50 | * Retrieves all places. 51 | * @param resultHandler the result handler 52 | */ 53 | public void getAllPlaces(Handler>>> resultHandler) { 54 | this.delegate.getAllPlaces(new Handler>>() { 55 | public void handle(AsyncResult> event) { 56 | AsyncResult>> f 57 | if (event.succeeded()) { 58 | f = InternalHelper.>>result(event.result().collect({ 59 | io.vertx.workshop.data.Place element -> 60 | (Map)InternalHelper.wrapObject(element?.toJson()) 61 | }) as List) 62 | } else { 63 | f = InternalHelper.>>failure(event.cause()) 64 | } 65 | resultHandler.handle(f) 66 | } 67 | }); 68 | } 69 | /** 70 | * Retrieves all places belonging to the given category. 71 | * @param category the category 72 | * @param resultHandler the result handler 73 | */ 74 | public void getPlacesForCategory(String category, Handler>>> resultHandler) { 75 | this.delegate.getPlacesForCategory(category, new Handler>>() { 76 | public void handle(AsyncResult> event) { 77 | AsyncResult>> f 78 | if (event.succeeded()) { 79 | f = InternalHelper.>>result(event.result().collect({ 80 | io.vertx.workshop.data.Place element -> 81 | (Map)InternalHelper.wrapObject(element?.toJson()) 82 | }) as List) 83 | } else { 84 | f = InternalHelper.>>failure(event.cause()) 85 | } 86 | resultHandler.handle(f) 87 | } 88 | }); 89 | } 90 | /** 91 | * Retrieves all places belonging with the given tag. 92 | * @param tag the tag 93 | * @param resultHandler the result handler 94 | */ 95 | public void getPlacesForTag(String tag, Handler>>> resultHandler) { 96 | this.delegate.getPlacesForTag(tag, new Handler>>() { 97 | public void handle(AsyncResult> event) { 98 | AsyncResult>> f 99 | if (event.succeeded()) { 100 | f = InternalHelper.>>result(event.result().collect({ 101 | io.vertx.workshop.data.Place element -> 102 | (Map)InternalHelper.wrapObject(element?.toJson()) 103 | }) as List) 104 | } else { 105 | f = InternalHelper.>>failure(event.cause()) 106 | } 107 | resultHandler.handle(f) 108 | } 109 | }); 110 | } 111 | /** 112 | * Adds a place in the data storage service. 113 | * @param place the place (see Place) 114 | * @param resultHandler handler called when the insertion is done. 115 | */ 116 | public void addPlace(Map place = [:], Handler> resultHandler) { 117 | this.delegate.addPlace(place != null ? new io.vertx.workshop.data.Place(new io.vertx.core.json.JsonObject(place)) : null, resultHandler); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /data-storage-service/src/main/java/io/vertx/workshop/data/DataStorageService.java: -------------------------------------------------------------------------------- 1 | package io.vertx.workshop.data; 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.Vertx; 8 | import io.vertx.serviceproxy.ProxyHelper; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Service exposed on the event bus to provide access to 14 | * the stored Places. 15 | */ 16 | @VertxGen 17 | @ProxyGen 18 | public interface DataStorageService { 19 | 20 | /** 21 | * Method called to create a proxy (to consume the service). 22 | * 23 | * @param vertx vert.x 24 | * @param address the address on the vent bus where the service is served. 25 | * @return the proxy on the {@link DataStorageService} 26 | */ 27 | static DataStorageService createProxy(Vertx vertx, String address) { 28 | return ProxyHelper.createProxy(DataStorageService.class, vertx, address); 29 | } 30 | 31 | /** 32 | * Retrieves all places. 33 | * 34 | * @param resultHandler the result handler 35 | */ 36 | void getAllPlaces(Handler>> resultHandler); 37 | 38 | /** 39 | * Retrieves all places belonging to the given category. 40 | * 41 | * @param category the category 42 | * @param resultHandler the result handler 43 | */ 44 | void getPlacesForCategory(String category, Handler>> resultHandler); 45 | 46 | /** 47 | * Retrieves all places belonging with the given tag. 48 | * 49 | * @param tag the tag 50 | * @param resultHandler the result handler 51 | */ 52 | void getPlacesForTag(String tag, Handler>> resultHandler); 53 | 54 | /** 55 | * Adds a place in the data storage service. 56 | * 57 | * @param place the place 58 | * @param resultHandler handler called when the insertion is done. 59 | */ 60 | void addPlace(Place place, Handler> resultHandler); 61 | } 62 | -------------------------------------------------------------------------------- /data-storage-service/src/main/java/io/vertx/workshop/data/Place.java: -------------------------------------------------------------------------------- 1 | package io.vertx.workshop.data; 2 | 3 | import io.vertx.codegen.annotations.DataObject; 4 | import io.vertx.core.json.JsonObject; 5 | 6 | import java.util.*; 7 | 8 | /** 9 | * The place structure. 10 | * 11 | * It's a data object that can be serialized to JSON (and built from JSON). Data objects can be used in event bus 12 | * proxies. 13 | */ 14 | @DataObject(generateConverter = true) 15 | public class Place { 16 | 17 | private String name; 18 | 19 | private String description; 20 | 21 | private String category; 22 | 23 | private Set tags = new TreeSet<>(); 24 | 25 | private double longitude; 26 | 27 | private double latitude; 28 | 29 | private String address; 30 | 31 | public Place() { 32 | // Empty constructor 33 | } 34 | 35 | public Place(Place other) { 36 | this.name = other.name; 37 | this.description = other.description; 38 | this.category = other.category; 39 | this.tags = other.tags; 40 | this.longitude = other.longitude; 41 | this.latitude = other.latitude; 42 | this.address = other.address; 43 | } 44 | 45 | public Place(JsonObject json) { 46 | PlaceConverter.fromJson(json, this); 47 | } 48 | 49 | public JsonObject toJson() { 50 | JsonObject json = new JsonObject(); 51 | PlaceConverter.toJson(this, json); 52 | return json; 53 | } 54 | 55 | public String getAddress() { 56 | return address; 57 | } 58 | 59 | public Place setAddress(String address) { 60 | this.address = address; 61 | return this; 62 | } 63 | 64 | public String getCategory() { 65 | return category; 66 | } 67 | 68 | public Place setCategory(String category) { 69 | this.category = category; 70 | return this; 71 | } 72 | 73 | public String getDescription() { 74 | return description; 75 | } 76 | 77 | public Place setDescription(String description) { 78 | this.description = description; 79 | return this; 80 | } 81 | 82 | public double getLatitude() { 83 | return latitude; 84 | } 85 | 86 | public Place setLatitude(double latitude) { 87 | this.latitude = latitude; 88 | return this; 89 | } 90 | 91 | public double getLongitude() { 92 | return longitude; 93 | } 94 | 95 | public Place setLongitude(double longitude) { 96 | this.longitude = longitude; 97 | return this; 98 | } 99 | 100 | public String getName() { 101 | return name; 102 | } 103 | 104 | public Place setName(String name) { 105 | this.name = name; 106 | return this; 107 | } 108 | 109 | public Set getTags() { 110 | return tags; 111 | } 112 | 113 | public Place setTags(List tags) { 114 | this.tags = new TreeSet<>(tags); 115 | return this; 116 | } 117 | 118 | public Place addTag(String tag) { 119 | tags.add(tag); 120 | return this; 121 | } 122 | 123 | public Place removeTag(String tag) { 124 | tags.remove(tag); 125 | return this; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /data-storage-service/src/main/java/io/vertx/workshop/data/impl/DataStorageServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.vertx.workshop.data.impl; 2 | 3 | import io.vertx.core.AsyncResult; 4 | import io.vertx.core.Future; 5 | import io.vertx.core.Handler; 6 | import io.vertx.core.Vertx; 7 | import io.vertx.core.json.JsonObject; 8 | import io.vertx.workshop.data.DataStorageService; 9 | import io.vertx.workshop.data.Place; 10 | import io.vertx.ext.mongo.MongoClient; 11 | 12 | import java.util.List; 13 | import java.util.logging.Logger; 14 | import java.util.stream.Collectors; 15 | 16 | /** 17 | * Implementation of the {@link DataStorageService}. 18 | */ 19 | public class DataStorageServiceImpl implements DataStorageService { 20 | 21 | public static final String COLLECTION = "places"; 22 | public static final Logger LOGGER = Logger.getLogger("Data Storage Service"); 23 | 24 | private final MongoClient mongo; 25 | private final Vertx vertx; 26 | 27 | public DataStorageServiceImpl(Vertx vertx, JsonObject config) { 28 | this.mongo = MongoClient.createShared(vertx, config, "places"); 29 | this.vertx = vertx; 30 | LOGGER.info("Data Storage Service instantiated"); 31 | } 32 | 33 | @Override 34 | public void getAllPlaces(Handler>> resultHandler) { 35 | /** 36 | * TODO to implement. 37 | */ 38 | } 39 | 40 | public void getPlacesForCategory(String category, 41 | Handler>> resultHandler) { 42 | mongo.find(COLLECTION, 43 | new JsonObject().put("category", category), 44 | ar -> { 45 | if (ar.failed()) { 46 | resultHandler.handle(Future.failedFuture(ar.cause())); 47 | } else { 48 | List places = ar.result().stream() 49 | .map(Place::new).collect(Collectors.toList()); 50 | resultHandler.handle(Future.succeededFuture(places)); 51 | } 52 | } 53 | ); 54 | } 55 | 56 | 57 | @Override 58 | public void getPlacesForTag(String tag, Handler>> resultHandler) { 59 | /** 60 | * TODO to implement. 61 | */ 62 | } 63 | 64 | @Override 65 | public void addPlace(Place place, Handler> resultHandler) { 66 | /** 67 | * TODO to implement. 68 | */ 69 | } 70 | 71 | public void close() { 72 | mongo.close(); 73 | } 74 | 75 | 76 | private void report(long time) { 77 | vertx.eventBus().send("metrics", new JsonObject().put("source", "mongo.query").put("value", time)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /data-storage-service/src/main/java/io/vertx/workshop/data/impl/DataStorageVerticle.java: -------------------------------------------------------------------------------- 1 | package io.vertx.workshop.data.impl; 2 | 3 | import io.vertx.core.AbstractVerticle; 4 | import io.vertx.workshop.data.DataStorageService; 5 | import io.vertx.serviceproxy.ProxyHelper; 6 | 7 | /** 8 | * The verticle instantiating the service. 9 | */ 10 | public class DataStorageVerticle extends AbstractVerticle { 11 | 12 | private DataStorageServiceImpl service; 13 | 14 | @Override 15 | public void start() throws Exception { 16 | service = new DataStorageServiceImpl(vertx, config()); 17 | //TODO Change address to something more specific 18 | ProxyHelper.registerService(DataStorageService.class, vertx, service, "devoxx.places"); 19 | } 20 | 21 | @Override 22 | public void stop() throws Exception { 23 | service.close(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /data-storage-service/src/main/java/io/vertx/workshop/data/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * File configuring the code generation. 3 | */ 4 | @ModuleGen(name="vertx-microservice-workshop", groupPackage = "io.vertx.workshop") 5 | package io.vertx.workshop.data; 6 | 7 | import io.vertx.codegen.annotations.ModuleGen; -------------------------------------------------------------------------------- /data-storage-service/src/main/resources/vertx-microservice-workshop-js/data_storage_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 | /** @module vertx-microservice-workshop-js/data_storage_service */ 18 | !function (factory) { 19 | if (typeof require === 'function' && typeof module !== 'undefined') { 20 | factory(); 21 | } else if (typeof define === 'function' && define.amd) { 22 | // AMD loader 23 | define('vertx-microservice-workshop-js/data_storage_service-proxy', [], factory); 24 | } else { 25 | // plain old include 26 | DataStorageService = factory(); 27 | } 28 | }(function () { 29 | 30 | /** 31 | Service exposed on the event bus to provide access to 32 | the stored Places. 33 | 34 | @class 35 | */ 36 | var DataStorageService = function(eb, address) { 37 | 38 | var j_eb = eb; 39 | var j_address = address; 40 | var closed = false; 41 | var that = this; 42 | var convCharCollection = function(coll) { 43 | var ret = []; 44 | for (var i = 0;i < coll.length;i++) { 45 | ret.push(String.fromCharCode(coll[i])); 46 | } 47 | return ret; 48 | }; 49 | 50 | /** 51 | Retrieves all places. 52 | 53 | @public 54 | @param resultHandler {function} the result handler 55 | */ 56 | this.getAllPlaces = function(resultHandler) { 57 | var __args = arguments; 58 | if (__args.length === 1 && typeof __args[0] === 'function') { 59 | if (closed) { 60 | throw new Error('Proxy is closed'); 61 | } 62 | j_eb.send(j_address, {}, {"action":"getAllPlaces"}, function(err, result) { __args[0](err, result &&result.body); }); 63 | return; 64 | } else throw new TypeError('function invoked with invalid arguments'); 65 | }; 66 | 67 | /** 68 | Retrieves all places belonging to the given category. 69 | 70 | @public 71 | @param category {string} the category 72 | @param resultHandler {function} the result handler 73 | */ 74 | this.getPlacesForCategory = function(category, resultHandler) { 75 | var __args = arguments; 76 | if (__args.length === 2 && typeof __args[0] === 'string' && typeof __args[1] === 'function') { 77 | if (closed) { 78 | throw new Error('Proxy is closed'); 79 | } 80 | j_eb.send(j_address, {"category":__args[0]}, {"action":"getPlacesForCategory"}, function(err, result) { __args[1](err, result &&result.body); }); 81 | return; 82 | } else throw new TypeError('function invoked with invalid arguments'); 83 | }; 84 | 85 | /** 86 | Retrieves all places belonging with the given tag. 87 | 88 | @public 89 | @param tag {string} the tag 90 | @param resultHandler {function} the result handler 91 | */ 92 | this.getPlacesForTag = function(tag, resultHandler) { 93 | var __args = arguments; 94 | if (__args.length === 2 && typeof __args[0] === 'string' && typeof __args[1] === 'function') { 95 | if (closed) { 96 | throw new Error('Proxy is closed'); 97 | } 98 | j_eb.send(j_address, {"tag":__args[0]}, {"action":"getPlacesForTag"}, function(err, result) { __args[1](err, result &&result.body); }); 99 | return; 100 | } else throw new TypeError('function invoked with invalid arguments'); 101 | }; 102 | 103 | /** 104 | Adds a place in the data storage service. 105 | 106 | @public 107 | @param place {Object} the place 108 | @param resultHandler {function} handler called when the insertion is done. 109 | */ 110 | this.addPlace = function(place, resultHandler) { 111 | var __args = arguments; 112 | if (__args.length === 2 && typeof __args[0] === 'object' && typeof __args[1] === 'function') { 113 | if (closed) { 114 | throw new Error('Proxy is closed'); 115 | } 116 | j_eb.send(j_address, {"place":__args[0]}, {"action":"addPlace"}, function(err, result) { __args[1](err, result &&result.body); }); 117 | return; 118 | } else throw new TypeError('function invoked with invalid arguments'); 119 | }; 120 | 121 | }; 122 | 123 | /** 124 | Method called to create a proxy (to consume the service). 125 | 126 | @memberof module:vertx-microservice-workshop-js/data_storage_service 127 | @param vertx {Vertx} vert.x 128 | @param address {string} the address on the vent bus where the service is served. 129 | @return {DataStorageService} the proxy on the {@link DataStorageService} 130 | */ 131 | DataStorageService.createProxy = function(vertx, address) { 132 | var __args = arguments; 133 | if (__args.length === 2 && typeof __args[0] === 'object' && __args[0]._jdel && typeof __args[1] === 'string') { 134 | if (closed) { 135 | throw new Error('Proxy is closed'); 136 | } 137 | j_eb.send(j_address, {"vertx":__args[0], "address":__args[1]}, {"action":"createProxy"}); 138 | return; 139 | } else throw new TypeError('function invoked with invalid arguments'); 140 | }; 141 | 142 | if (typeof exports !== 'undefined') { 143 | if (typeof module !== 'undefined' && module.exports) { 144 | exports = module.exports = DataStorageService; 145 | } else { 146 | exports.DataStorageService = DataStorageService; 147 | } 148 | } else { 149 | return DataStorageService; 150 | } 151 | }); -------------------------------------------------------------------------------- /data-storage-service/src/main/resources/vertx-microservice-workshop-js/data_storage_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-microservice-workshop-js/data_storage_service */ 18 | var utils = require('vertx-js/util/utils'); 19 | var Vertx = require('vertx-js/vertx'); 20 | 21 | var io = Packages.io; 22 | var JsonObject = io.vertx.core.json.JsonObject; 23 | var JDataStorageService = io.vertx.workshop.data.DataStorageService; 24 | var Place = io.vertx.workshop.data.Place; 25 | 26 | /** 27 | Service exposed on the event bus to provide access to 28 | the stored Places. 29 | 30 | @class 31 | */ 32 | var DataStorageService = function(j_val) { 33 | 34 | var j_dataStorageService = j_val; 35 | var that = this; 36 | 37 | /** 38 | Retrieves all places. 39 | 40 | @public 41 | @param resultHandler {function} the result handler 42 | */ 43 | this.getAllPlaces = function(resultHandler) { 44 | var __args = arguments; 45 | if (__args.length === 1 && typeof __args[0] === 'function') { 46 | j_dataStorageService["getAllPlaces(io.vertx.core.Handler)"](function(ar) { 47 | if (ar.succeeded()) { 48 | resultHandler(utils.convReturnListSetDataObject(ar.result()), null); 49 | } else { 50 | resultHandler(null, ar.cause()); 51 | } 52 | }); 53 | } else throw new TypeError('function invoked with invalid arguments'); 54 | }; 55 | 56 | /** 57 | Retrieves all places belonging to the given category. 58 | 59 | @public 60 | @param category {string} the category 61 | @param resultHandler {function} the result handler 62 | */ 63 | this.getPlacesForCategory = function(category, resultHandler) { 64 | var __args = arguments; 65 | if (__args.length === 2 && typeof __args[0] === 'string' && typeof __args[1] === 'function') { 66 | j_dataStorageService["getPlacesForCategory(java.lang.String,io.vertx.core.Handler)"](category, function(ar) { 67 | if (ar.succeeded()) { 68 | resultHandler(utils.convReturnListSetDataObject(ar.result()), null); 69 | } else { 70 | resultHandler(null, ar.cause()); 71 | } 72 | }); 73 | } else throw new TypeError('function invoked with invalid arguments'); 74 | }; 75 | 76 | /** 77 | Retrieves all places belonging with the given tag. 78 | 79 | @public 80 | @param tag {string} the tag 81 | @param resultHandler {function} the result handler 82 | */ 83 | this.getPlacesForTag = function(tag, resultHandler) { 84 | var __args = arguments; 85 | if (__args.length === 2 && typeof __args[0] === 'string' && typeof __args[1] === 'function') { 86 | j_dataStorageService["getPlacesForTag(java.lang.String,io.vertx.core.Handler)"](tag, function(ar) { 87 | if (ar.succeeded()) { 88 | resultHandler(utils.convReturnListSetDataObject(ar.result()), null); 89 | } else { 90 | resultHandler(null, ar.cause()); 91 | } 92 | }); 93 | } else throw new TypeError('function invoked with invalid arguments'); 94 | }; 95 | 96 | /** 97 | Adds a place in the data storage service. 98 | 99 | @public 100 | @param place {Object} the place 101 | @param resultHandler {function} handler called when the insertion is done. 102 | */ 103 | this.addPlace = function(place, resultHandler) { 104 | var __args = arguments; 105 | if (__args.length === 2 && typeof __args[0] === 'object' && typeof __args[1] === 'function') { 106 | j_dataStorageService["addPlace(io.vertx.workshop.data.Place,io.vertx.core.Handler)"](place != null ? new Place(new JsonObject(JSON.stringify(place))) : null, function(ar) { 107 | if (ar.succeeded()) { 108 | resultHandler(null, null); 109 | } else { 110 | resultHandler(null, ar.cause()); 111 | } 112 | }); 113 | } else throw new TypeError('function invoked with invalid arguments'); 114 | }; 115 | 116 | // A reference to the underlying Java delegate 117 | // NOTE! This is an internal API and must not be used in user code. 118 | // If you rely on this property your code is likely to break if we change it / remove it without warning. 119 | this._jdel = j_dataStorageService; 120 | }; 121 | 122 | /** 123 | Method called to create a proxy (to consume the service). 124 | 125 | @memberof module:vertx-microservice-workshop-js/data_storage_service 126 | @param vertx {Vertx} vert.x 127 | @param address {string} the address on the vent bus where the service is served. 128 | @return {DataStorageService} the proxy on the {@link DataStorageService} 129 | */ 130 | DataStorageService.createProxy = function(vertx, address) { 131 | var __args = arguments; 132 | if (__args.length === 2 && typeof __args[0] === 'object' && __args[0]._jdel && typeof __args[1] === 'string') { 133 | return utils.convReturnVertxGen(JDataStorageService["createProxy(io.vertx.core.Vertx,java.lang.String)"](vertx._jdel, address), DataStorageService); 134 | } else throw new TypeError('function invoked with invalid arguments'); 135 | }; 136 | 137 | // We export the Constructor function 138 | module.exports = DataStorageService; -------------------------------------------------------------------------------- /etc/cluster.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | false 7 | false 8 | false 9 | 0 10 | jdk 11 | 12 | 13 | 14 | dev 15 | dev-pass 16 | 17 | http://localhost:8080/mancenter 18 | 19 | 5701 20 | 21 | 25 | 0 26 | 27 | 28 | 29 | 224.2.2.3 30 | 54327 31 | 32 | 33 | 192.168.1.28 34 | 35 | 36 | my-access-key 37 | my-secret-key 38 | 39 | us-west-1 40 | 41 | ec2.amazonaws.com 42 | 43 | hazelcast-sg 44 | type 45 | hz-nodes 46 | 47 | 48 | 49 | 50 | 51 | 192.168.1.* 52 | 53 | 54 | 55 | 56 | 57 | 65 | PBEWithMD5AndDES 66 | 67 | thesalt 68 | 69 | thepass 70 | 71 | 19 72 | 73 | 74 | 75 | 76 | 16 77 | 78 | 0 79 | 80 | 81 | 82 | 87 | 1 88 | 94 | 0 95 | 101 | 0 102 | 109 | NONE 110 | 116 | 0 117 | 123 | 25 124 | 139 | com.hazelcast.map.merge.LatestUpdateMapMergePolicy 140 | 141 | 142 | 143 | 144 | 145 | 1 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /etc/vertx-default-jul-logging-graylog.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copy it to src/main/resources, rename it to vertx-default-jul-logging.properties 3 | # 4 | handlers=java.util.logging.ConsoleHandler,java.util.logging.FileHandler,biz.paluch.logging.gelf.jul.GelfLogHandler 5 | java.util.logging.SimpleFormatter.format=%5$s %6$s\n 6 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 7 | java.util.logging.ConsoleHandler.level=INFO 8 | java.util.logging.FileHandler.level=INFO 9 | java.util.logging.FileHandler.formatter=io.vertx.core.logging.impl.VertxLoggerFormatter 10 | biz.paluch.logging.gelf.jul.GelfLogHandler.level=INFO 11 | 12 | # Put the log in the system temporary directory 13 | java.util.logging.FileHandler.pattern=%t/vertx.log 14 | 15 | .level=INFO 16 | io.vertx.level=INFO 17 | com.hazelcast.level=SEVERE 18 | io.netty.util.internal.PlatformDependent.level=SEVERE 19 | 20 | biz.paluch.logging.gelf.jul.GelfLogHandler.host=192.168.99.100 21 | biz.paluch.logging.gelf.jul.GelfLogHandler.port=12201 22 | biz.paluch.logging.gelf.jul.GelfLogHandler.version=1.1 -------------------------------------------------------------------------------- /eventbus-to-hawkular-bridge/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | io.vertx.workshop 9 | vertx-microservice-workshop 10 | 1.0-SNAPSHOT 11 | 12 | 13 | eventbus-to-hawkular-bridge 14 | Eventbus to Hawkular bridge 15 | 16 | 17 | io.vertx.workshop.hawkular.HawkularBridge 18 | 19 | 20 | 21 | 22 | org.hawkular.metrics 23 | clients-common 24 | 0.6.0.Final 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-shade-plugin 33 | 2.3 34 | 35 | 36 | package 37 | 38 | shade 39 | 40 | 41 | 42 | 43 | 44 | io.vertx.core.Launcher 45 | ${main.verticle} 46 | 47 | 48 | 49 | META-INF/services/io.vertx.core.spi.VerticleFactory 50 | 51 | 52 | META-INF/services/io.vertx.core.spi.VertxMetricsFactory 53 | 54 | 55 | 56 | 57 | ${project.build.directory}/${project.artifactId}-${project.version}-fat.jar 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /eventbus-to-hawkular-bridge/src/conf/config-docker-machine.json: -------------------------------------------------------------------------------- 1 | { 2 | "hawkular.host": "192.168.99.100", 3 | "hawkular.port": 8090 4 | } -------------------------------------------------------------------------------- /eventbus-to-hawkular-bridge/src/conf/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "hawkular.host": "localhost", 3 | "hawkular.port": 8090 4 | } -------------------------------------------------------------------------------- /eventbus-to-hawkular-bridge/src/main/java/io/vertx/workshop/hawkular/HawkularBridge.java: -------------------------------------------------------------------------------- 1 | package io.vertx.workshop.hawkular; 2 | 3 | import io.vertx.core.AbstractVerticle; 4 | import io.vertx.core.Launcher; 5 | import io.vertx.core.buffer.Buffer; 6 | import io.vertx.core.http.HttpClient; 7 | import io.vertx.core.http.HttpClientOptions; 8 | import io.vertx.core.http.HttpClientRequest; 9 | import io.vertx.core.json.JsonObject; 10 | import org.hawkular.metrics.client.common.Batcher; 11 | import org.hawkular.metrics.client.common.SingleMetric; 12 | 13 | import java.util.Collections; 14 | import java.util.List; 15 | import java.util.logging.Logger; 16 | 17 | /** 18 | * @author Clement Escoffier 19 | */ 20 | public class HawkularBridge extends AbstractVerticle { 21 | 22 | private HttpClient httpClient; 23 | 24 | private static final String URL = "/hawkular/metrics/gauges/data"; 25 | private static final Logger LOGGER = Logger.getLogger(HawkularBridge.class.getName()); 26 | 27 | public static void main(String[] args) { 28 | new Launcher().execute("run", HawkularBridge.class.getName()); 29 | } 30 | 31 | /** 32 | * If your verticle does a simple, synchronous start-up then override this method and put your start-up 33 | * code in there. 34 | * 35 | * @throws Exception 36 | */ 37 | @Override 38 | public void start() throws Exception { 39 | httpClient = vertx.createHttpClient(new HttpClientOptions() 40 | .setDefaultPort(config().getInteger("hawkular.port", 8090)) 41 | .setDefaultHost(config().getString("hawkular.host", "192.168.99.100"))); 42 | 43 | vertx.eventBus().consumer("metrics", msg -> { 44 | JsonObject json = (JsonObject) msg.body(); 45 | String source = json.getString("source"); 46 | double value = json.getDouble("value"); 47 | SingleMetric simple = new SingleMetric(source, System.currentTimeMillis(), value); 48 | send(Collections.singletonList(simple)); 49 | }); 50 | } 51 | 52 | private void send(List metrics) { 53 | String json = Batcher.metricListToJson(metrics); 54 | Buffer buffer = Buffer.buffer(json); 55 | HttpClientRequest req = httpClient.post( 56 | URL, 57 | response -> { 58 | LOGGER.info("Metric sent " + response.statusCode() + " / " + metrics.get(0).toJson()); 59 | if (response.statusCode() != 200) { 60 | response.bodyHandler(msg -> LOGGER.warning("Could not send metrics: " + response.statusCode() + " : " 61 | + msg.toString())); 62 | } 63 | }); 64 | req.putHeader("Content-Length", String.valueOf(buffer.length())); 65 | req.putHeader("Content-Type", "application/json"); 66 | req.putHeader("Hawkular-Tenant", "default"); 67 | req.exceptionHandler(err -> LOGGER.severe("Could not send metrics " + err)); 68 | req.write(buffer); 69 | req.end(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /frontend/Readme.md: -------------------------------------------------------------------------------- 1 | # Frontend 2 | 3 | This is a web frontend showing that you can also just use JavaScript and NPM. 4 | 5 | ## Run 6 | 7 | ``` 8 | npm install 9 | npm start 10 | ``` -------------------------------------------------------------------------------- /frontend/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | io.vertx.workshop 9 | vertx-microservice-workshop 10 | 1.0-SNAPSHOT 11 | 12 | 13 | frontend 14 | vert2go - Frontend 15 | 16 | 17 | server.js 18 | 19 | 20 | 21 | 22 | ${project.groupId} 23 | data-storage-service 24 | ${project.version} 25 | 26 | 27 | io.vertx 28 | vertx-lang-js 29 | 30 | 31 | io.vertx 32 | vertx-web 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-shade-plugin 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-dependency-plugin 45 | 46 | 47 | unpack 48 | generate-resources 49 | 50 | unpack 51 | 52 | 53 | 54 | 55 | ${project.groupId} 56 | recommendation-service 57 | ${project.version} 58 | jar 59 | true 60 | ${project.build.directory}/classes/webroot/js/vendor 61 | **/*-proxy.js 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /frontend/src/main/resources/server.js: -------------------------------------------------------------------------------- 1 | var Router = require('vertx-web-js/router'); 2 | var SockJSHandler = require('vertx-web-js/sock_js_handler'); 3 | var StaticHandler = require('vertx-web-js/static_handler'); 4 | var DataStorageService = require('vertx-microservice-workshop-js/data_storage_service'); 5 | 6 | // create a http router 7 | var router = Router.router(vertx); 8 | 9 | // create services 10 | var store = DataStorageService.createProxy(vertx, 'devoxx.places'); 11 | 12 | // Allow events for the designated addresses in/out of the event bus bridge 13 | var opts = { 14 | inboundPermitteds: [{address: 'devoxx.recommendations'}], 15 | outboundPermitteds: [{address: 'devoxx.recommendations.announce'}] 16 | }; 17 | 18 | // Create the event bus bridge and add it to the router. 19 | router.route('/eventbus/*').handler(SockJSHandler.create(vertx).bridge(opts).handle); 20 | 21 | // handle request to places 22 | router.get('/places').handler(function (ctx) { 23 | store.getAllPlaces(function (res, err) { 24 | if (err) { 25 | ctx.fail(err); 26 | } else { 27 | ctx.response().putHeader('Content-Type', 'application/json').end(JSON.stringify(res)); 28 | } 29 | }) 30 | }); 31 | 32 | // Serve the static resources 33 | router.route().handler(StaticHandler.create().handle); 34 | 35 | vertx.createHttpServer().requestHandler(router.accept).listen(8080); -------------------------------------------------------------------------------- /frontend/src/main/resources/webroot/css/app.css: -------------------------------------------------------------------------------- 1 | /* app css stylesheet */ 2 | html, body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | padding-top: 50px; /*padding for navbar*/ 8 | } 9 | 10 | .navbar-custom { 11 | background-color: #168ccc; 12 | color: #fff; 13 | } 14 | 15 | .navbar-custom li > a:hover, .navbar-custom li > a:focus { 16 | background-color: #49bfff; 17 | } 18 | 19 | .navbar-custom a { 20 | color: #fefefe; 21 | } 22 | 23 | .navbar-custom .form-control:focus { 24 | border-color: #49bfff; 25 | outline: 0; 26 | -webkit-box-shadow: inset 0 0 0; 27 | box-shadow: inset 0 0 0; 28 | } 29 | 30 | #main, #main > .row { 31 | height: 100%; 32 | } 33 | 34 | #main > .row { 35 | overflow-y: scroll; 36 | } 37 | 38 | #left { 39 | height: 100%; 40 | } 41 | 42 | #map { 43 | width: 66.6666%; 44 | height: calc(100% - 0); 45 | position: absolute; 46 | right: 16px; 47 | top: 50px; 48 | bottom: 0; 49 | overflow: hidden; 50 | } -------------------------------------------------------------------------------- /frontend/src/main/resources/webroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | vert.x - vert2go 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 39 | 40 |
41 |
42 |
43 | 44 |

Points of Interest

45 | 46 |
47 |
48 |
49 | {{poi.name}} 50 |
51 |
52 | 53 | {{tag}} 54 | 55 |
56 |
57 |

{{poi.description}}

58 |

Recommend:

59 | {{poi.thumbsUp}}
60 | {{poi.thumbsDown}} 61 |
62 |
63 |
64 | 65 |
66 |
67 |
68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /frontend/src/main/resources/webroot/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* App Module */ 4 | 5 | var app = angular.module('poiApp', []); 6 | -------------------------------------------------------------------------------- /frontend/src/main/resources/webroot/js/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Controllers */ 4 | 5 | app.controller('POIListController', function ($scope, $http) { 6 | 7 | // TODO Configuration: 8 | // Change here if you are using the docker-machine: http:////192.168.99.100:8000 9 | // Change here if you are using localhost: http://localhost:8000 10 | var proxyURL = "http://localhost:8000"; 11 | 12 | // TODO Change recommendation service address to your own 13 | var recommendationServiceAddress = "devoxx.recommendations"; 14 | var recommendationBroadcastAddress = "devoxx.recommendations.announce"; 15 | 16 | // place map in antwerpen center 17 | var map = L.map('map').setView([51.21796, 4.42079], 13); 18 | var marker; 19 | 20 | 21 | L.tileLayer(proxyURL + '/{z}/{x}/{y}.png', { 22 | maxZoom: 18, 23 | attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA' 24 | }).addTo(map); 25 | 26 | // bridge directly to the recommendation service 27 | var eb = new EventBus(window.location.protocol + '//' + window.location.host + '/eventbus'); 28 | var recommendation = new RecommendationService(eb, recommendationServiceAddress); 29 | 30 | // initialize the POIs 31 | $scope.pois = []; 32 | 33 | // connect to the event bus 34 | eb.onopen = function () { 35 | // request the resource from the server 36 | $http.get('http://localhost:8080/places').success(function (data) { 37 | // load recommendations 38 | data.forEach(function (el) { 39 | $scope.pois.push(el); 40 | 41 | recommendation.get(el.name, function (err, res) { 42 | if (!err) { 43 | $scope.$apply(function () { 44 | el.thumbsUp = res.up || el.thumbsUp; 45 | el.thumbsDown = res.down || el.thumbsDown; 46 | el.thumbs = (el.thumbsUp || 0) - (el.thumbsDown || 0); 47 | }); 48 | } 49 | }); 50 | }); 51 | 52 | eb.registerHandler(recommendationBroadcastAddress,function (err, msg) { 53 | if (!err) { 54 | $scope.$apply(function () { 55 | $scope.pois.filter(function (val) { 56 | return val.name === msg.body.name; 57 | }).forEach(function (el) { 58 | el.thumbsUp = msg.body.up || el.thumbsUp; 59 | el.thumbsDown = msg.body.down || el.thumbsDown; 60 | el.thumbs = (el.thumbsUp || 0) - (el.thumbsDown || 0); 61 | }); 62 | }); 63 | } 64 | }); 65 | }); 66 | }; 67 | 68 | // declare helpers 69 | $scope.showInMap = function (poi) { 70 | if (marker) { 71 | marker.setLatLng([poi.latitude, poi.longitude]); 72 | } else { 73 | marker = L.marker([poi.latitude, poi.longitude]).addTo(map); 74 | } 75 | 76 | marker.bindPopup("" + poi.name + "
" + poi.address).openPopup(); 77 | map.setView([poi.latitude, poi.longitude], 16); 78 | }; 79 | 80 | $scope.thumbs = function (poi, up) { 81 | recommendation.vote(poi.name, up, function (err) { 82 | if (err) { 83 | console.error(err); 84 | } 85 | }); 86 | }; 87 | }); -------------------------------------------------------------------------------- /frontend/src/main/resources/webroot/js/vendor/vertx-eventbus.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2015 The original author or authors 3 | * ------------------------------------------------------ 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Apache License v2.0 which accompanies this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * 11 | * The Apache License v2.0 is available at 12 | * http://www.opensource.org/licenses/apache2.0.php 13 | * 14 | * You may elect to redistribute this code under either of these licenses. 15 | */ 16 | !function (factory) { 17 | if (typeof require === 'function' && typeof module !== 'undefined') { 18 | // CommonJS loader 19 | var SockJS = require('sockjs-client'); 20 | if(!SockJS) { 21 | throw new Error('vertx-eventbus.js requires sockjs-client, see http://sockjs.org'); 22 | } 23 | factory(SockJS); 24 | } else if (typeof define === 'function' && define.amd) { 25 | // AMD loader 26 | define('vertx-eventbus', ['sockjs'], factory); 27 | } else { 28 | // plain old include 29 | if (typeof this.SockJS === 'undefined') { 30 | throw new Error('vertx-eventbus.js requires sockjs-client, see http://sockjs.org'); 31 | } 32 | 33 | EventBus = factory(this.SockJS); 34 | } 35 | }(function (SockJS) { 36 | 37 | function makeUUID() { 38 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (a, b) { 39 | return b = Math.random() * 16, (a == 'y' ? b & 3 | 8 : b | 0).toString(16); 40 | }); 41 | } 42 | 43 | function mergeHeaders(defaultHeaders, headers) { 44 | if (defaultHeaders) { 45 | if(!headers) { 46 | return defaultHeaders; 47 | } 48 | 49 | for (var headerName in defaultHeaders) { 50 | if (defaultHeaders.hasOwnProperty(headerName)) { 51 | // user can overwrite the default headers 52 | if (typeof headers[headerName] === 'undefined') { 53 | headers[headerName] = defaultHeaders[headerName]; 54 | } 55 | } 56 | } 57 | } 58 | 59 | // headers are required to be a object 60 | return headers || {}; 61 | } 62 | 63 | /** 64 | * EventBus 65 | * 66 | * @param url 67 | * @param options 68 | * @constructor 69 | */ 70 | var EventBus = function (url, options) { 71 | var self = this; 72 | 73 | options = options || {}; 74 | 75 | var pingInterval = options.vertxbus_ping_interval || 5000; 76 | var pingTimerID; 77 | 78 | // attributes 79 | this.sockJSConn = new SockJS(url, null, options); 80 | this.state = EventBus.CONNECTING; 81 | this.handlers = {}; 82 | this.replyHandlers = {}; 83 | this.defaultHeaders = null; 84 | 85 | // default event handlers 86 | this.onerror = function (err) { 87 | try { 88 | console.error(err); 89 | } catch (e) { 90 | // dev tools are disabled so we cannot use console on IE 91 | } 92 | }; 93 | 94 | var sendPing = function () { 95 | self.sockJSConn.send(JSON.stringify({type: 'ping'})); 96 | }; 97 | 98 | this.sockJSConn.onopen = function () { 99 | // Send the first ping then send a ping every pingInterval milliseconds 100 | sendPing(); 101 | pingTimerID = setInterval(sendPing, pingInterval); 102 | self.state = EventBus.OPEN; 103 | self.onopen && self.onopen(); 104 | }; 105 | 106 | this.sockJSConn.onclose = function () { 107 | self.state = EventBus.CLOSED; 108 | if (pingTimerID) clearInterval(pingTimerID); 109 | self.onclose && self.onclose(); 110 | }; 111 | 112 | this.sockJSConn.onmessage = function (e) { 113 | var json = JSON.parse(e.data); 114 | 115 | // define a reply function on the message itself 116 | if (json.replyAddress) { 117 | Object.defineProperty(json, 'reply', { 118 | value: function (message, headers, callback) { 119 | self.send(json.replyAddress, message, headers, callback); 120 | } 121 | }); 122 | } 123 | 124 | if (self.handlers[json.address]) { 125 | // iterate all registered handlers 126 | var handlers = self.handlers[json.address]; 127 | for (var i = 0; i < handlers.length; i++) { 128 | if (json.type === 'err') { 129 | handlers[i]({failureCode: json.failureCode, failureType: json.failureType, message: json.message}); 130 | } else { 131 | handlers[i](null, json); 132 | } 133 | } 134 | } else if (self.replyHandlers[json.address]) { 135 | // Might be a reply message 136 | var handler = self.replyHandlers[json.address]; 137 | delete self.replyHandlers[json.address]; 138 | if (json.type === 'err') { 139 | handler({failureCode: json.failureCode, failureType: json.failureType, message: json.message}); 140 | } else { 141 | handler(null, json); 142 | } 143 | } else { 144 | if (json.type === 'err') { 145 | self.onerror(json); 146 | } else { 147 | try { 148 | console.warn('No handler found for message: ', json); 149 | } catch (e) { 150 | // dev tools are disabled so we cannot use console on IE 151 | } 152 | } 153 | } 154 | } 155 | }; 156 | 157 | /** 158 | * Send a message 159 | * 160 | * @param {String} address 161 | * @param {Object} message 162 | * @param {Object} [headers] 163 | * @param {Function} [callback] 164 | */ 165 | EventBus.prototype.send = function (address, message, headers, callback) { 166 | // are we ready? 167 | if (this.state != EventBus.OPEN) { 168 | throw new Error('INVALID_STATE_ERR'); 169 | } 170 | 171 | if (typeof headers === 'function') { 172 | callback = headers; 173 | headers = {}; 174 | } 175 | 176 | var envelope = { 177 | type: 'send', 178 | address: address, 179 | headers: mergeHeaders(this.defaultHeaders, headers), 180 | body: message 181 | }; 182 | 183 | if (callback) { 184 | var replyAddress = makeUUID(); 185 | envelope.replyAddress = replyAddress; 186 | this.replyHandlers[replyAddress] = callback; 187 | } 188 | 189 | this.sockJSConn.send(JSON.stringify(envelope)); 190 | }; 191 | 192 | /** 193 | * Publish a message 194 | * 195 | * @param {String} address 196 | * @param {Object} message 197 | * @param {Object} [headers] 198 | */ 199 | EventBus.prototype.publish = function (address, message, headers) { 200 | // are we ready? 201 | if (this.state != EventBus.OPEN) { 202 | throw new Error('INVALID_STATE_ERR'); 203 | } 204 | 205 | this.sockJSConn.send(JSON.stringify({ 206 | type: 'publish', 207 | address: address, 208 | headers: mergeHeaders(this.defaultHeaders, headers), 209 | body: message 210 | })); 211 | }; 212 | 213 | /** 214 | * Register a new handler 215 | * 216 | * @param {String} address 217 | * @param {Object} [headers] 218 | * @param {Function} callback 219 | */ 220 | EventBus.prototype.registerHandler = function (address, headers, callback) { 221 | // are we ready? 222 | if (this.state != EventBus.OPEN) { 223 | throw new Error('INVALID_STATE_ERR'); 224 | } 225 | 226 | if (typeof headers === 'function') { 227 | callback = headers; 228 | headers = {}; 229 | } 230 | 231 | // ensure it is an array 232 | if (!this.handlers[address]) { 233 | this.handlers[address] = []; 234 | // First handler for this address so we should register the connection 235 | this.sockJSConn.send(JSON.stringify({ 236 | type: 'register', 237 | address: address, 238 | headers: mergeHeaders(this.defaultHeaders, headers) 239 | })); 240 | } 241 | 242 | this.handlers[address].push(callback); 243 | }; 244 | 245 | /** 246 | * Unregister a handler 247 | * 248 | * @param {String} address 249 | * @param {Object} [headers] 250 | * @param {Function} callback 251 | */ 252 | EventBus.prototype.unregisterHandler = function (address, headers, callback) { 253 | // are we ready? 254 | if (this.state != EventBus.OPEN) { 255 | throw new Error('INVALID_STATE_ERR'); 256 | } 257 | 258 | var handlers = this.handlers[address]; 259 | 260 | if (handlers) { 261 | 262 | if (typeof headers === 'function') { 263 | callback = headers; 264 | headers = {}; 265 | } 266 | 267 | var idx = handlers.indexOf(callback); 268 | if (idx != -1) { 269 | handlers.splice(idx, 1); 270 | if (handlers.length === 0) { 271 | // No more local handlers so we should unregister the connection 272 | this.sockJSConn.send(JSON.stringify({ 273 | type: 'unregister', 274 | address: address, 275 | headers: mergeHeaders(this.defaultHeaders, headers) 276 | })); 277 | 278 | delete this.handlers[address]; 279 | } 280 | } 281 | } 282 | }; 283 | 284 | /** 285 | * Closes the connection to the EvenBus Bridge. 286 | */ 287 | EventBus.prototype.close = function () { 288 | this.state = EventBus.CLOSING; 289 | this.sockJSConn.close(); 290 | }; 291 | 292 | EventBus.CONNECTING = 0; 293 | EventBus.OPEN = 1; 294 | EventBus.CLOSING = 2; 295 | EventBus.CLOSED = 3; 296 | 297 | if (typeof exports !== 'undefined') { 298 | if (typeof module !== 'undefined' && module.exports) { 299 | exports = module.exports = EventBus; 300 | } else { 301 | exports.EventBus = EventBus; 302 | } 303 | } else { 304 | return EventBus; 305 | } 306 | }); -------------------------------------------------------------------------------- /map-render-service/Readme.md: -------------------------------------------------------------------------------- 1 | # Map Render Verticle 2 | 3 | This is a simple demo of a verticle that consumes messages addressed to "map-render" on a REST api and generates a PNG 4 | tile according to the slippy map spec (e.g.: google maps, openstreepmap, etc...) 5 | 6 | In order to speedup the startup a index of a area to render should be created before hand. A index is just a serialized 7 | version of a OSM data extract. see [wiki](http://wiki.openstreetmap.org/wiki/Downloading_data). 8 | 9 | 10 | ## Openshift 11 | 12 | ``` 13 | rhc create-app map0render0service https://raw.githubusercontent.com/vert-x3/vertx-openshift-cartridge/master/metadata/manifest.yml 14 | ``` 15 | 16 | **Deployment:** 17 | 18 | ``` 19 | cd openshift 20 | git add -A 21 | git commit -m "deploy my application" 22 | git push 23 | ``` 24 | -------------------------------------------------------------------------------- /map-render-service/openshift/.openshift/README.md: -------------------------------------------------------------------------------- 1 | The OpenShift `mock` cartridge documentation can be found at: 2 | https://github.com/openshift/origin-server/blob/master/cartridges/openshift-origin-cartridge-mock/README.md 3 | 4 | For information about .openshift directory, consult the documentation: 5 | http://openshift.github.io/documentation/oo_user_guide.html#the-openshift-directory 6 | -------------------------------------------------------------------------------- /map-render-service/openshift/.openshift/action_hooks/README.md: -------------------------------------------------------------------------------- 1 | For information about action hooks, consult the documentation: 2 | 3 | http://openshift.github.io/documentation/oo_user_guide.html#action-hooks 4 | -------------------------------------------------------------------------------- /map-render-service/openshift/.openshift/markers/README.md: -------------------------------------------------------------------------------- 1 | For information about markers, consult the documentation: 2 | 3 | http://openshift.github.io/documentation/oo_user_guide.html#markers 4 | -------------------------------------------------------------------------------- /map-render-service/openshift/application/README.md: -------------------------------------------------------------------------------- 1 | Copy your application fat jar in this directory. 2 | -------------------------------------------------------------------------------- /map-render-service/openshift/configuration/vertx.env: -------------------------------------------------------------------------------- 1 | # Pass java system properties via VERTX_OPTS 2 | # E.g. export VERTX_OPTS="-Dsome.prop=blah" 3 | export VERTX_OPTS="" 4 | # Pass application argument via APP_ARGS 5 | # E.g. export RUN_ARGS="-conf ${OPENSHIFT_REPO_DIR}/config.json" 6 | # If you want HAZELCAST_CLUSTERING don't remove the `-cp` value 7 | export RUN_ARGS="-cp $OPENSHIFT_VERTX_DIR/conf/cluster.xml:$OPENSHIFT_VERTX_DIR/conf" 8 | 9 | # To enable Hazelcast clustering 10 | # (require having created the application with `-s`) 11 | # export HAZELCAST_CLUSTERING=true 12 | -------------------------------------------------------------------------------- /map-render-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | 8 | 9 | io.vertx.workshop 10 | vertx-microservice-workshop 11 | 1.0-SNAPSHOT 12 | 13 | 14 | map-render-service 15 | Map Render Service 16 | 17 | 18 | io.vertx.workshop.map.render.WebMapRenderVerticle 19 | 20 | 21 | 22 | 23 | ${project.groupId} 24 | data-storage-service 25 | ${project.version} 26 | 27 | 28 | io.vertx 29 | vertx-web 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-shade-plugin 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-antrun-plugin 42 | 1.8 43 | 44 | 45 | package 46 | 47 | 48 | 50 | 51 | 52 | 53 | run 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/BoundingBox.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * BoundingBox implementation. 10 | * 11 | * @see Minimum bounding box 12 | * @author Paulo Lopes 13 | */ 14 | public class BoundingBox implements Serializable { 15 | 16 | private static final long serialVersionUID = 1L; 17 | 18 | private double minLat; 19 | private double minLon; 20 | private double maxLat; 21 | private double maxLon; 22 | 23 | /** 24 | * Create a new Bounding Box filled with 0s 25 | */ 26 | public BoundingBox() { 27 | // default Bounding box 28 | } 29 | 30 | /** 31 | * Create a Bounding box with the given coordinates. 32 | * 33 | * @param minlat min latitude 34 | * @param minlon min longitude 35 | * @param maxlat max latitude 36 | * @param maxlon max longitude 37 | */ 38 | public BoundingBox(double minlat, double minlon, double maxlat, double maxlon) { 39 | this.minLat = minlat; 40 | this.minLon = minlon; 41 | this.maxLat = maxlat; 42 | this.maxLon = maxlon; 43 | } 44 | 45 | public double getMinLat() { 46 | return minLat; 47 | } 48 | 49 | public void setMinLat(double minLat) { 50 | this.minLat = minLat; 51 | } 52 | 53 | public double getMinLon() { 54 | return minLon; 55 | } 56 | 57 | public void setMinLon(double minLon) { 58 | this.minLon = minLon; 59 | } 60 | 61 | public double getMaxLat() { 62 | return maxLat; 63 | } 64 | 65 | public void setMaxLat(double maxLat) { 66 | this.maxLat = maxLat; 67 | } 68 | 69 | public double getMaxLon() { 70 | return maxLon; 71 | } 72 | 73 | public void setMaxLon(double maxLon) { 74 | this.maxLon = maxLon; 75 | } 76 | 77 | public double getNorth() { 78 | return maxLat; 79 | } 80 | 81 | public double getSouth() { 82 | return minLat; 83 | } 84 | 85 | public double getWest() { 86 | return minLon; 87 | } 88 | 89 | public double getEast() { 90 | return maxLon; 91 | } 92 | 93 | public void setNorth(double north) { 94 | maxLat = north; 95 | } 96 | 97 | public void setSouth(double south) { 98 | minLat = south; 99 | } 100 | 101 | public void setWest(double west) { 102 | minLon = west; 103 | } 104 | 105 | public void setEast(double east) { 106 | maxLon = east; 107 | } 108 | 109 | public boolean intersects(BoundingBox b) { 110 | return 111 | (minLat <= b.maxLat) && 112 | (maxLat >= b.minLat) && 113 | (minLon <= b.maxLon) && 114 | (maxLon >= b.minLon); 115 | } 116 | 117 | @Override 118 | public String toString() { 119 | return "{" + minLat + ":" + minLon + "," + maxLat + ":" + maxLon + "}"; 120 | } 121 | 122 | public BoundingBox getNWQuadrant() { 123 | double hLatDiff = (maxLat - minLat) / 2.0; 124 | double hLonDiff = (maxLon - minLon) / 2.0; 125 | 126 | return new BoundingBox(minLat, minLon, minLat + hLatDiff, minLon + hLonDiff); 127 | } 128 | 129 | public BoundingBox getNEQuadrant() { 130 | double hLatDiff = (maxLat - minLat) / 2.0; 131 | double hLonDiff = (maxLon - minLon) / 2.0; 132 | 133 | return new BoundingBox(minLat, minLon + hLonDiff, minLat + hLatDiff, maxLon); 134 | } 135 | 136 | public BoundingBox getSWQuadrant() { 137 | double hLatDiff = (maxLat - minLat) / 2.0; 138 | double hLonDiff = (maxLon - minLon) / 2.0; 139 | 140 | return new BoundingBox(minLat + hLatDiff, minLon, maxLat, minLon + hLonDiff); 141 | } 142 | 143 | public BoundingBox getSEQuadrant() { 144 | double hLatDiff = (maxLat - minLat) / 2.0; 145 | double hLonDiff = (maxLon - minLon) / 2.0; 146 | 147 | return new BoundingBox(minLat + hLatDiff, minLon + hLonDiff, maxLat, maxLon); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/Coordinate.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * Implement a coordinate 10 | * 11 | * @see Coordinate System 12 | * @author Paulo Lopes 13 | */ 14 | public class Coordinate implements Serializable { 15 | 16 | private static final long serialVersionUID = 1L; 17 | 18 | public final double x; 19 | public final double y; 20 | 21 | public Coordinate(final double x, final double y) { 22 | this.x = x; 23 | this.y = y; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "<" + x + ":" + y + ">"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/MapException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map; 5 | 6 | /** 7 | * Generic Map related exception. 8 | * 9 | * @author Paulo Lopes 10 | */ 11 | @SuppressWarnings("serial") 12 | public final class MapException extends Exception { 13 | 14 | public MapException(String message) { 15 | super(message); 16 | } 17 | 18 | public MapException(Throwable cause) { 19 | super(cause); 20 | } 21 | 22 | public MapException(String message, Throwable cause) { 23 | super(message, cause); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/index/QTree.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.index; 5 | 6 | import java.io.Serializable; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | 10 | import io.vertx.workshop.map.BoundingBox; 11 | 12 | /** 13 | * QuadTree implementation. 14 | * 15 | * @author Paulo Lopes 16 | * @see Quadtree. 17 | * 18 | * @param a type extending BoundingBox 19 | */ 20 | public class QTree implements Serializable { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | private final BoundingBox bb; 25 | private final int treeDepth; 26 | private final QTree nw; 27 | private final QTree ne; 28 | private final QTree sw; 29 | private final QTree se; 30 | private List elements; 31 | 32 | /** 33 | * Create a QuadTree partition using a max depth level of 6. 34 | * 35 | * @param bbox the space to partition. 36 | */ 37 | public QTree(BoundingBox bbox) { 38 | this(6, bbox); 39 | } 40 | 41 | /** 42 | * Create a QuadTree with a specific level of depth. Note that the implementation is not Lazy, meaning that all quads 43 | * are instantiated at the time of construction. Having this knowledge in mind be aware choosing a high level of 44 | * depth will have the side effect of using much more memory and high levels of recursion. 45 | * 46 | * @param maxTreeDepth the max allowed sub partition level 47 | * @param bbox the space to partition 48 | */ 49 | public QTree(int maxTreeDepth, BoundingBox bbox) { 50 | bb = bbox; 51 | treeDepth = maxTreeDepth; 52 | 53 | if (treeDepth > 1) { 54 | nw = new QTree<>(treeDepth - 1, bb.getNWQuadrant()); 55 | ne = new QTree<>(treeDepth - 1, bb.getNEQuadrant()); 56 | sw = new QTree<>(treeDepth - 1, bb.getSWQuadrant()); 57 | se = new QTree<>(treeDepth - 1, bb.getSEQuadrant()); 58 | } else { 59 | nw = null; 60 | ne = null; 61 | sw = null; 62 | se = null; 63 | } 64 | } 65 | 66 | /** 67 | * Add an element to the tree. 68 | * 69 | * @param t the element 70 | */ 71 | public void add(T t) { 72 | if (treeDepth == 1) { 73 | if (t.intersects(bb)) { 74 | if (elements == null) { 75 | elements = new LinkedList<>(); 76 | } 77 | elements.add(t); 78 | } else { 79 | throw new UnsupportedOperationException("Element outside BoundingBox: " + t); 80 | } 81 | return; 82 | } 83 | 84 | boolean bNW = t.intersects(nw.getBoundingBox()); 85 | boolean bNE = t.intersects(ne.getBoundingBox()); 86 | boolean bSW = t.intersects(sw.getBoundingBox()); 87 | boolean bSE = t.intersects(se.getBoundingBox()); 88 | 89 | int nCount = 0; 90 | 91 | if (bNW) { 92 | nCount++; 93 | } 94 | if (bNE) { 95 | nCount++; 96 | } 97 | if (bSW) { 98 | nCount++; 99 | } 100 | if (bSE) { 101 | nCount++; 102 | } 103 | 104 | if (nCount > 1) { 105 | if (elements == null) { 106 | elements = new LinkedList<>(); 107 | } 108 | elements.add(t); 109 | return; 110 | } 111 | if (nCount == 0) { 112 | throw new UnsupportedOperationException("Element outside BoundingBox: " + t); 113 | } 114 | 115 | if (bNW) { 116 | nw.add(t); 117 | } 118 | if (bNE) { 119 | ne.add(t); 120 | } 121 | if (bSW) { 122 | sw.add(t); 123 | } 124 | if (bSE) { 125 | se.add(t); 126 | } 127 | } 128 | 129 | /** 130 | * Remove a element from the tree. 131 | * 132 | * @param t the element 133 | * @return true if the element existed before in the tree 134 | */ 135 | public boolean remove(T t) { 136 | if (elements != null && elements.remove(t)) { 137 | return true; 138 | } 139 | 140 | if (treeDepth > 1) { 141 | if (nw.remove(t)) { 142 | return true; 143 | } 144 | 145 | if (ne.remove(t)) { 146 | return true; 147 | } 148 | 149 | if (sw.remove(t)) { 150 | return true; 151 | } 152 | 153 | if (se.remove(t)) { 154 | return true; 155 | } 156 | } 157 | 158 | return false; 159 | } 160 | 161 | /** 162 | * Clear all data from the tree. 163 | */ 164 | public void clear() { 165 | if (elements != null) elements.clear(); 166 | 167 | if (treeDepth > 1) { 168 | nw.clear(); 169 | ne.clear(); 170 | sw.clear(); 171 | se.clear(); 172 | } 173 | } 174 | 175 | /** 176 | * Accessor for the max level for this tree. 177 | * 178 | * @return the level. 179 | */ 180 | public int getMaxTreeDepth() { 181 | return treeDepth; 182 | } 183 | 184 | public List getAll() { 185 | List l = new LinkedList<>(); 186 | if (elements != null) { 187 | l.addAll(elements); 188 | } 189 | 190 | if (treeDepth > 1) { 191 | l.addAll(nw.getAll()); 192 | l.addAll(ne.getAll()); 193 | l.addAll(sw.getAll()); 194 | l.addAll(se.getAll()); 195 | } 196 | 197 | return l; 198 | } 199 | 200 | /** 201 | * Get all elements in the tree that are contained in the given bounding box. The test for inclusion is intersect, 202 | * this means that if just 1 point of a element falls inside the given bounding box it the element will be returned. 203 | * 204 | * @param bbox the filtering window 205 | * @return all elements that intersect the window 206 | */ 207 | public List get(BoundingBox bbox) { 208 | List v = new LinkedList<>(); 209 | 210 | if (bb.intersects(bbox)) { 211 | if (elements != null) { 212 | for (T t : elements) { 213 | if (t.intersects(bbox)) { 214 | v.add(t); 215 | } 216 | } 217 | } 218 | if (treeDepth > 1) { 219 | v.addAll(nw.get(bbox)); 220 | v.addAll(ne.get(bbox)); 221 | v.addAll(sw.get(bbox)); 222 | v.addAll(se.get(bbox)); 223 | } 224 | } 225 | 226 | return v; 227 | } 228 | 229 | /** 230 | * Accessor for the bounding box. 231 | * 232 | * @return the bounding box. 233 | */ 234 | public BoundingBox getBoundingBox() { 235 | return bb; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/mercator/Mercator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.mercator; 5 | 6 | import static java.lang.StrictMath.PI; 7 | import static java.lang.StrictMath.atan; 8 | import static java.lang.StrictMath.cos; 9 | import static java.lang.StrictMath.floor; 10 | import static java.lang.StrictMath.log; 11 | import static java.lang.StrictMath.pow; 12 | import static java.lang.StrictMath.sinh; 13 | import static java.lang.StrictMath.tan; 14 | import static java.lang.StrictMath.toDegrees; 15 | import static java.lang.StrictMath.toRadians; 16 | 17 | import io.vertx.workshop.map.BoundingBox; 18 | import io.vertx.workshop.map.Coordinate; 19 | 20 | /** 21 | * Utility class to handle the Mercator projection calculations. 22 | * 23 | * @see Mercator projection 24 | * @author Paulo Lopes 25 | */ 26 | public final class Mercator { 27 | 28 | private Mercator() { 29 | // disable instantiation 30 | } 31 | 32 | private static int numTiles(double z) { 33 | return (int) pow(2.0, z); 34 | } 35 | 36 | private static double sec(double x) { 37 | return (1.0 / cos(x)); 38 | } 39 | 40 | private static double mercatorToLat(double y) { 41 | return toDegrees(atan(sinh(y))); 42 | } 43 | 44 | /** 45 | * Convert a lat/lon to x,y based on the Mercator Projection 46 | * 47 | * @param lat latitude 48 | * @param lon longitude 49 | * @param z zoom level 50 | * @param tilesize size of the generated tiles 51 | * 52 | * @return coordinate 53 | */ 54 | public static Coordinate coord2xy(double lat, double lon, int z, int tilesize) { 55 | int numTiles_by_tilesize = numTiles(z) * tilesize; 56 | double rad_lat = toRadians(lat); 57 | 58 | return new Coordinate( 59 | numTiles_by_tilesize * (lon + 180.0) / 360.0, 60 | numTiles_by_tilesize * (1.0 - log(tan(rad_lat) + sec(rad_lat)) / PI) / 2.0); 61 | } 62 | 63 | /** 64 | * Convert a lat/lon to x,y based on the Mercator Projection without accounting for the tile size and zoom level. 65 | * 66 | * @param lat latitude 67 | * @param lon longitude 68 | * 69 | * @return coordinate 70 | */ 71 | public static Coordinate latlon2relativeXY(double lat, double lon) { 72 | double rad_lat = toRadians(lat); 73 | 74 | return new Coordinate( 75 | (lon + 180.0) / 360.0, 76 | (1 - log(tan(rad_lat) + sec(rad_lat)) / PI) / 2.0); 77 | } 78 | 79 | /** 80 | * Convert a lat/lon to x,y based on the Mercator Projection without accounting for the tile size. 81 | * 82 | * @param lat latitude 83 | * @param lon longitude 84 | * @param z zoom level 85 | * 86 | * @return coordinate 87 | */ 88 | public static Coordinate latlon2xy(double lat, double lon, int z) { 89 | int numTiles = numTiles(z); 90 | double rad_lat = toRadians(lat); 91 | 92 | return new Coordinate( 93 | numTiles * (lon + 180.0) / 360.0, 94 | numTiles * (1 - log(tan(rad_lat) + sec(rad_lat)) / PI) / 2); 95 | } 96 | 97 | /** 98 | * Calculate the edge of a tile given an arbitrary y and zoom level. 99 | * @param y y 100 | * @param z zoom level 101 | * @return calculate the edges 102 | */ 103 | public static Coordinate latEdges(int y, int z) { 104 | double unit = 1.0 / numTiles(z); 105 | 106 | return new Coordinate( 107 | mercatorToLat(PI * (1.0 - 2.0 * (y * unit))), 108 | mercatorToLat(PI * (1.0 - 2.0 * (y * unit + unit)))); 109 | } 110 | 111 | /** 112 | * Calculate the edge of a tile given an arbitrary x and zoom level. 113 | * @param x x 114 | * @param z zoom level 115 | * @return calculate the edges 116 | */ 117 | public static Coordinate lonEdges(int x, int z) { 118 | double unit = 360.0 / numTiles(z); 119 | 120 | return new Coordinate( 121 | -180.0 + (x * unit), 122 | -180.0 + (x * unit) + unit); 123 | } 124 | 125 | /** 126 | * Determine the bounding box of a tile given an arbitrary x, y, z 127 | * @param x x 128 | * @param y y 129 | * @param z zoom level 130 | * @return calculate the bounding box 131 | */ 132 | public static BoundingBox tileEdges(int x, int y, int z) { 133 | BoundingBox result = new BoundingBox(); 134 | Coordinate ret; 135 | 136 | ret = latEdges(y, z); 137 | result.setNorth(ret.x); 138 | result.setSouth(ret.y); 139 | 140 | ret = lonEdges(x, z); 141 | result.setWest(ret.x); 142 | result.setEast(ret.y); 143 | 144 | return result; 145 | } 146 | 147 | /** 148 | * converts 'slippy maps' tile number to lat & lon in degrees 149 | */ 150 | public static Coordinate tile2latlon(int x, int y, int z) { 151 | int n = numTiles(z); 152 | double lat_rad = atan(sinh(PI * (1.0 - 2.0 * y / n))); 153 | 154 | return new Coordinate( 155 | lat_rad * 180.0 / PI, 156 | (double) x / (double) n * 360.0 - 180.0); 157 | } 158 | 159 | /** 160 | * converts lon in degrees to a 'slippy maps' x tile number 161 | */ 162 | public static int lon2tilex(double lon_deg, int z) { 163 | double ret = (lon_deg + 180.0) / 360.0 * numTiles(z); 164 | 165 | return (int) floor(ret); 166 | } 167 | 168 | /** 169 | * converts lat in degrees to a 'slippy maps' y tile number 170 | */ 171 | public static int lat2tiley(double lat_deg, int z) { 172 | int n = numTiles(z); 173 | double lat_rad = toRadians(lat_deg); 174 | double ret = (1.0 - log(tan(lat_rad) + sec(lat_rad)) / PI) / 2.0 * n; 175 | 176 | return (int) floor(ret); 177 | } 178 | } -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/render/MapRenderVerticle.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.render; 5 | 6 | import io.vertx.workshop.map.render.backend.Renderer; 7 | import io.vertx.workshop.map.rules.RuleSet; 8 | import io.vertx.workshop.map.source.MapSource; 9 | import io.vertx.core.AbstractVerticle; 10 | import io.vertx.core.MultiMap; 11 | import io.vertx.core.buffer.Buffer; 12 | 13 | import java.io.ByteArrayOutputStream; 14 | import java.io.IOException; 15 | 16 | /** 17 | * Map Tile Render Micro-Service 18 | * @author Paulo Lopes 19 | */ 20 | public class MapRenderVerticle extends AbstractVerticle { 21 | 22 | // ThreadSafe map renderer 23 | private static final Renderer renderer; 24 | 25 | static { 26 | try { 27 | 28 | final String rule = System.getProperty("rule", "rule.xml"); 29 | final String index = System.getProperty("index", "antwerpen.osm.idx.gz"); 30 | 31 | RuleSet ruleset = new RuleSet(MapRenderVerticle.class.getClassLoader().getResourceAsStream(rule)); 32 | MapSource map = new MapSource(MapRenderVerticle.class.getClassLoader().getResourceAsStream(index)); 33 | renderer = new Renderer(ruleset, map); 34 | } catch (Exception e) { 35 | throw new RuntimeException(e); 36 | } 37 | } 38 | 39 | @Override 40 | public void start() { 41 | 42 | vertx.eventBus().consumer("map-render", msg -> { 43 | try { 44 | final MultiMap headers = msg.headers(); 45 | 46 | int z = Integer.parseInt(headers.get("z")); 47 | int x = Integer.parseInt(headers.get("x")); 48 | int y = Integer.parseInt(headers.get("y")); 49 | 50 | final ByteArrayOutputStream out = new ByteArrayOutputStream(32 * 1024); 51 | renderer.drawTile(out, x, y, z); 52 | 53 | msg.reply(Buffer.buffer(out.toByteArray())); 54 | 55 | } catch (NumberFormatException | NullPointerException | IOException e) { 56 | e.printStackTrace(); 57 | msg.reply(null); 58 | } 59 | }); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/render/WebMapRenderVerticle.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.render; 5 | 6 | import io.vertx.core.AbstractVerticle; 7 | import io.vertx.core.buffer.Buffer; 8 | import io.vertx.workshop.map.render.backend.Renderer; 9 | import io.vertx.workshop.map.rules.RuleSet; 10 | import io.vertx.workshop.map.source.MapSource; 11 | import io.vertx.ext.web.Router; 12 | 13 | import java.io.ByteArrayOutputStream; 14 | import java.io.IOException; 15 | 16 | /** 17 | * Map Tile Render Micro-Service 18 | * 19 | * @author Paulo Lopes 20 | */ 21 | public class WebMapRenderVerticle extends AbstractVerticle { 22 | 23 | private static final Renderer renderer; 24 | 25 | static { 26 | try { 27 | 28 | final String rule = System.getProperty("rule", "rule.xml"); 29 | final String index = System.getProperty("index", "antwerpen.osm.idx.gz"); 30 | 31 | RuleSet ruleset = new RuleSet(WebMapRenderVerticle.class.getClassLoader().getResourceAsStream(rule)); 32 | MapSource map = new MapSource(WebMapRenderVerticle.class.getClassLoader().getResourceAsStream(index)); 33 | renderer = new Renderer(ruleset, map); 34 | } catch (Exception e) { 35 | throw new RuntimeException(e); 36 | } 37 | } 38 | 39 | @Override 40 | public void start() { 41 | Router router = Router.router(vertx); 42 | router.route("/render/:x/:y/:z").handler( 43 | context -> { 44 | int x = Integer.parseInt(context.request().getParam("x")); 45 | int y = Integer.parseInt(context.request().getParam("y")); 46 | int z = Integer.parseInt(context.request().getParam("z")); 47 | System.out.println("Requested tile: x=" + x + ", y=" + y + ", z=" + z); 48 | 49 | final ByteArrayOutputStream out = new ByteArrayOutputStream(32 * 1024); 50 | try { 51 | renderer.drawTile(out, x, y, z); 52 | } catch (IOException e) { 53 | context.response().setStatusCode(400).end(e.getMessage()); 54 | } 55 | context.response().end(Buffer.buffer(out.toByteArray())); 56 | } 57 | ); 58 | 59 | vertx.createHttpServer().requestHandler(router::accept).listen( 60 | // Important for openshift: 61 | Integer.getInteger("http.port", 8001), System.getProperty("http.address", "localhost")); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/render/backend/MapSurface.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.render.backend; 5 | 6 | import static java.awt.RenderingHints.KEY_ANTIALIASING; 7 | import static java.awt.RenderingHints.KEY_COLOR_RENDERING; 8 | import static java.awt.RenderingHints.KEY_INTERPOLATION; 9 | import static java.awt.RenderingHints.KEY_RENDERING; 10 | import static java.awt.RenderingHints.KEY_STROKE_CONTROL; 11 | import static java.awt.RenderingHints.KEY_TEXT_ANTIALIASING; 12 | import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON; 13 | import static java.awt.RenderingHints.VALUE_COLOR_RENDER_QUALITY; 14 | import static java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC; 15 | import static java.awt.RenderingHints.VALUE_RENDER_QUALITY; 16 | import static java.awt.RenderingHints.VALUE_STROKE_NORMALIZE; 17 | import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_GASP; 18 | 19 | import java.awt.Color; 20 | import java.awt.Graphics2D; 21 | import java.awt.Image; 22 | import java.awt.RenderingHints; 23 | import java.awt.geom.GeneralPath; 24 | import java.awt.image.BufferedImage; 25 | import java.awt.image.RenderedImage; 26 | import java.io.FileOutputStream; 27 | import java.io.IOException; 28 | import java.io.OutputStream; 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | 32 | import io.vertx.workshop.map.BoundingBox; 33 | import io.vertx.workshop.map.Coordinate; 34 | 35 | import javax.imageio.ImageIO; 36 | 37 | /** 38 | * Map Surface is a rendering surface where maps can be rendered. 39 | * 40 | * @author Paulo Lopes 41 | */ 42 | public class MapSurface { 43 | 44 | private static final Map RENDER_HINTS = new HashMap<>(); 45 | 46 | private final Image surface; 47 | private final Graphics2D graphics; 48 | 49 | // image metadata (immutable) 50 | public final Coordinate offset; 51 | public final BoundingBox bounds; 52 | 53 | public final int zoomLevel; 54 | public final GeneralPath path; 55 | 56 | static { 57 | RENDER_HINTS.put(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON); 58 | RENDER_HINTS.put(KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY); 59 | RENDER_HINTS.put(KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC); 60 | RENDER_HINTS.put(KEY_RENDERING, VALUE_RENDER_QUALITY); 61 | RENDER_HINTS.put(KEY_STROKE_CONTROL, VALUE_STROKE_NORMALIZE); 62 | RENDER_HINTS.put(KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_GASP); 63 | } 64 | 65 | public MapSurface(int width, int height, Color bg, int zoom_level, Coordinate offset, BoundingBox bounds) { 66 | surface = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); 67 | graphics = (Graphics2D) surface.getGraphics(); 68 | path = new GeneralPath(); 69 | 70 | this.zoomLevel = zoom_level; 71 | this.offset = offset; 72 | this.bounds = bounds; 73 | 74 | graphics.setBackground(bg); 75 | graphics.clearRect(0, 0, width, height); 76 | 77 | graphics.setRenderingHints(RENDER_HINTS); 78 | } 79 | 80 | public Graphics2D getGraphics() { 81 | return graphics; 82 | } 83 | 84 | public void write(final String filename) throws IOException { 85 | ImageIO.write((RenderedImage) surface, "PNG", new FileOutputStream(filename)); 86 | } 87 | 88 | public void write(final OutputStream out) throws IOException { 89 | ImageIO.write((RenderedImage) surface, "PNG", out); 90 | } 91 | 92 | public void flush() { 93 | surface.flush(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/render/backend/Renderer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.render.backend; 5 | 6 | import static java.lang.StrictMath.pow; 7 | 8 | import java.awt.BasicStroke; 9 | import java.awt.Font; 10 | import java.awt.Graphics2D; 11 | import java.awt.Paint; 12 | import java.awt.Stroke; 13 | import java.awt.TexturePaint; 14 | import java.awt.geom.Rectangle2D; 15 | import java.awt.image.BufferedImage; 16 | import java.io.IOException; 17 | import java.io.OutputStream; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | import javax.imageio.ImageIO; 23 | 24 | import io.vertx.workshop.map.BoundingBox; 25 | import io.vertx.workshop.map.Coordinate; 26 | import io.vertx.workshop.map.MapException; 27 | import io.vertx.workshop.map.mercator.Mercator; 28 | import io.vertx.workshop.map.rules.Draw; 29 | import io.vertx.workshop.map.rules.Rule; 30 | import io.vertx.workshop.map.rules.RuleSet; 31 | import io.vertx.workshop.map.source.MapSource; 32 | import io.vertx.workshop.map.source.Node; 33 | import io.vertx.workshop.map.source.Way; 34 | 35 | /** 36 | * Renderer is where all rendering actions take place 37 | * 38 | * @author Paulo Lopes 39 | */ 40 | public class Renderer { 41 | 42 | private static final int MIN_ZOOM_LEVEL = 12; 43 | private static final int MAX_ZOOM_LEVEL = 18; 44 | 45 | private static final int DEFAULT_RESOLUTION = 256; 46 | 47 | // icon/pattern management 48 | private static final Map TEX_CACHE = new HashMap<>(); 49 | private static final Map FNT_CACHE = new HashMap<>(); 50 | 51 | private static Paint getPaint(String pattern, Paint color) { 52 | if (null != pattern) { 53 | Paint paint = TEX_CACHE.get(pattern); 54 | if (null == paint) { 55 | try { 56 | BufferedImage texture = ImageIO.read(Renderer.class.getClassLoader().getResourceAsStream("pattern/" + pattern + ".png")); 57 | paint = new TexturePaint(texture, new Rectangle2D.Float(0, 0, texture.getWidth(), texture.getHeight())); 58 | } catch (IOException e) { 59 | paint = color; 60 | } 61 | TEX_CACHE.put(pattern, paint); 62 | } 63 | return paint; 64 | } 65 | return color; 66 | } 67 | 68 | @SuppressWarnings("boxing") 69 | private static Font getFont(int size) { 70 | if (size > 5) { 71 | Font font = FNT_CACHE.get(size); 72 | if (null == font) { 73 | font = new Font("SansSerif", Font.PLAIN, size); 74 | FNT_CACHE.put(size, font); 75 | } 76 | return font; 77 | } 78 | return null; 79 | } 80 | 81 | // render source data (final can be used by several threads) 82 | private final MapSource map; 83 | private final RuleSet rules; 84 | private final int resolution; 85 | 86 | // Utility function 87 | private static int clamp(int val, int min, int max) { 88 | if (val < min) { 89 | return min; 90 | } else if (val > max) { 91 | return max; 92 | } 93 | return val; 94 | } 95 | 96 | // utility function 97 | private static int linesize(int z) { 98 | return z < 12 ? 1 : z == 18 ? 6 : (int) (pow(2, z - 12) / (z - 12 + 1)); 99 | } 100 | 101 | /** 102 | * Creates a thread safe renderer 103 | * 104 | * @param rules RuleSet that describe how to render 105 | * @param map Parsed Map data 106 | * @throws MapException Bad parameters 107 | */ 108 | public Renderer(RuleSet rules, MapSource map) throws MapException { 109 | this(rules, map, DEFAULT_RESOLUTION); 110 | } 111 | 112 | /** 113 | * Creates a thread safe renderer 114 | * 115 | * @param rules RuleSet that describe how to render 116 | * @param map Parsed Map data 117 | * @param resolution size of the tile 118 | * @throws MapException Bad parameters 119 | */ 120 | public Renderer(RuleSet rules, MapSource map, int resolution) throws MapException { 121 | if (rules == null || map == null) { 122 | throw new MapException("No map and/or rules data"); 123 | } 124 | 125 | this.map = map; 126 | this.rules = rules; 127 | this.resolution = resolution; 128 | } 129 | 130 | public void drawTile(final OutputStream out, final int x, final int y, final int zoom_level) throws IOException { 131 | 132 | int c_zoom_level = clamp(zoom_level, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL); 133 | Coordinate crd = Mercator.tile2latlon(x, y, c_zoom_level); 134 | 135 | Coordinate offset = Mercator.coord2xy(crd.x, crd.y, c_zoom_level, resolution); 136 | BoundingBox bbox = Mercator.tileEdges(x, y, c_zoom_level); 137 | 138 | MapSurface surface = new MapSurface(resolution, resolution, rules.getBgColor(), c_zoom_level, offset, bbox); 139 | List mapData = map.getWaysInBoundingBox(bbox); 140 | render(surface, mapData); 141 | 142 | surface.write(out); 143 | surface.flush(); 144 | } 145 | 146 | private void render(final MapSurface surface, final List mapData) { 147 | // Start checking osm from bottom layer. 148 | for (int layer = -5; layer <= 5; layer++) { 149 | // Process Rule by Rule 150 | for (Rule rule = rules.root(); rule != null; rule = rule.next()) { 151 | if (rule.getDraw() != null) { 152 | renderPaths(surface, layer, rule, rule.getDraw(), mapData); 153 | // FIXME: Text Rendering 154 | // renderText(surface, layer, rule, rule.getDraw(), mapData); 155 | } 156 | } 157 | } 158 | } 159 | 160 | private void renderPaths(MapSurface surface, int layer, Rule rule, Draw draw, final List mapData) { 161 | int paths = 0; 162 | Draw d = draw; 163 | surface.path.reset(); 164 | 165 | // Loop through ways for 166 | for (Way way : mapData) { 167 | // perform geometry culling. If an object is not on the current layer or 168 | // inside the tile bounding box, it can be skipped 169 | if (way.getLayer() != layer) continue; 170 | Map tags = way.getTags(); 171 | if (tags != null) { 172 | if (RuleSet.checkRule(rule, tags)) { 173 | paths += buildPath(surface, way.getWayNode()); 174 | } 175 | } 176 | } 177 | if (paths != 0) { 178 | 179 | Graphics2D graphics = surface.getGraphics(); 180 | 181 | while (d != null) { 182 | if (d.getMinZoom() > surface.zoomLevel || d.getMaxZoom() < surface.zoomLevel) { 183 | d = d.next(); 184 | continue; 185 | } 186 | switch (d.type) { 187 | case Draw.POLYGONE: 188 | graphics.setPaint(getPaint(d.getPattern(), d.getColor())); 189 | graphics.fill(surface.path); 190 | break; 191 | case Draw.LINE: 192 | float strokeWidth = d.getWidth() * linesize(surface.zoomLevel); 193 | if (strokeWidth > 0.5f) { 194 | Stroke stroke = new BasicStroke( 195 | strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); 196 | 197 | graphics.setPaint(d.getColor()); 198 | graphics.setStroke(stroke); 199 | graphics.draw(surface.path); 200 | } 201 | break; 202 | case Draw.TEXT: 203 | break; /* ignore since we will process it in other step */ 204 | } 205 | d = d.next(); 206 | } 207 | } 208 | } 209 | 210 | private int buildPath(MapSurface surface, List nodes) { 211 | Node nd = nodes.get(0); 212 | Coordinate xy0 = Mercator.coord2xy(nd.getLat(), nd.getLon(), surface.zoomLevel, resolution); 213 | surface.path.moveTo(xy0.x - surface.offset.x, xy0.y - surface.offset.y); 214 | 215 | Coordinate xy; 216 | int paths = 0; 217 | for (int i = 1; i < nodes.size(); i++) { 218 | nd = nodes.get(i); 219 | xy = Mercator.coord2xy(nd.getLat(), nd.getLon(), surface.zoomLevel, resolution); 220 | if (xy0.x != xy.x && xy0.y != xy.y) { 221 | surface.path.lineTo(xy.x - surface.offset.x, xy.y - surface.offset.y); 222 | paths++; 223 | } 224 | xy0 = xy; 225 | } 226 | return paths; 227 | } 228 | 229 | private void renderText(MapSurface surface, int layer, Rule rule, Draw draw, final List mapData) { 230 | Graphics2D graphics = surface.getGraphics(); 231 | Draw d = draw; 232 | 233 | while (d != null) { 234 | Font font = getFont((int) (d.getWidth() * linesize(surface.zoomLevel))); 235 | 236 | if (d.type == Draw.TEXT && font != null) { 237 | if (draw.getMinZoom() <= surface.zoomLevel && surface.zoomLevel <= draw.getMaxZoom()) { 238 | 239 | for (Way way : mapData) { 240 | // Only objects on current layer 241 | if (way.getLayer() != layer || way.getName() == null) 242 | continue; 243 | 244 | Map tags = way.getTags(); 245 | if (tags != null) { 246 | if (RuleSet.checkRule(rule, tags)) { 247 | // TODO: verify if there is a path and render text along the path? or see how osmarender does it? 248 | graphics.setFont(font); 249 | graphics.setPaint(d.getColor()); 250 | Node nd = way.getWayNode().get(0); 251 | Coordinate xy = Mercator.coord2xy(nd.getLat(), nd.getLon(), surface.zoomLevel, resolution); 252 | 253 | graphics.drawString( 254 | way.getName(), 255 | (int) (xy.x - surface.offset.x), 256 | (int) (xy.y - surface.offset.y)); 257 | } 258 | } 259 | } 260 | } 261 | break; 262 | } 263 | d = d.next(); 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/rules/Draw.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.rules; 5 | 6 | import java.awt.Color; 7 | 8 | /** 9 | * LinkedList Struct for Draws 10 | * 11 | * @author Paulo Lopes 12 | */ 13 | public class Draw { 14 | 15 | public static final int UNKNOWN = 0; 16 | public static final int LINE = 1; 17 | public static final int POLYGONE = 2; 18 | public static final int TEXT = 3; 19 | 20 | public final int type; 21 | 22 | private int minzoom; 23 | private int maxzoom; 24 | private Color color; 25 | private String pattern; 26 | private float width; 27 | 28 | Draw next; 29 | 30 | public Draw(int type) { 31 | this.type = type; 32 | maxzoom = 99; 33 | } 34 | 35 | public void setColor(Color c) { 36 | this.color = c; 37 | } 38 | 39 | public Color getColor() { 40 | return color; 41 | } 42 | 43 | public void setWidth(float w) { 44 | this.width = w; 45 | } 46 | 47 | public float getWidth() { 48 | return width; 49 | } 50 | 51 | public void setPattern(String pattern) { 52 | this.pattern = pattern; 53 | } 54 | 55 | public void setZoom(int min, int max) { 56 | this.minzoom = min; 57 | this.maxzoom = max; 58 | } 59 | 60 | public int getMinZoom() { 61 | return minzoom; 62 | } 63 | 64 | public int getMaxZoom() { 65 | return maxzoom; 66 | } 67 | 68 | public Draw next() { 69 | return next; 70 | } 71 | 72 | public String getPattern() { 73 | return pattern; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/rules/Rule.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.rules; 5 | 6 | /** 7 | * Defines a drawing rule for the #RuleSet. 8 | * 9 | * @author Paulo Lopes 10 | * @since 1.0 11 | */ 12 | public class Rule { 13 | 14 | public static final int UNKNOWN = 0; 15 | public static final int WAY = 1; 16 | public static final int NODE = 2; 17 | public static final int RELATION = 4; 18 | 19 | /* an array of key strings */ 20 | public final String[] keys; 21 | /* an array of value strings */ 22 | public final String[] values; 23 | /* the type of the rule */ 24 | public final int type; 25 | /* the mode of the rule */ 26 | public final boolean negate; 27 | 28 | /* Next terminal rule */ 29 | private Rule next; 30 | 31 | private Rule parent; 32 | 33 | private Draw draw; 34 | 35 | /** 36 | * Creates a new Rule 37 | * 38 | * @param type type of the rule 39 | * @param key the key of the rule 40 | * @param value the value of the rule 41 | */ 42 | public Rule(int type, String[] key, String[] value) { 43 | this(type, key, value, false); 44 | } 45 | 46 | /** 47 | * Create a new Rule 48 | * 49 | * @param type type of the rule 50 | * @param key the key of the rule 51 | * @param value the value of the rule 52 | * @param negate true for else rules, false for normal rules 53 | */ 54 | public Rule(int type, String[] key, String[] value, boolean negate) { 55 | this.type = type; 56 | this.keys = new String[key.length]; 57 | for (int i = 0; i < keys.length; i++) { 58 | keys[i] = key[i].intern(); 59 | } 60 | this.values = new String[value.length]; 61 | for (int i = 0; i < values.length; i++) { 62 | values[i] = value[i].intern(); 63 | } 64 | this.negate = negate; 65 | } 66 | 67 | public void addRule(Rule rule) { 68 | this.next = rule; 69 | } 70 | 71 | public void setParent(Rule parent) { 72 | this.parent = parent; 73 | } 74 | 75 | public Rule getParent() { 76 | return parent; 77 | } 78 | 79 | public void appendDraw(Draw draw) { 80 | Draw curr = this.draw; 81 | Draw prev = null; 82 | 83 | while (curr != null) { 84 | prev = curr; 85 | curr = curr.next; 86 | } 87 | if (prev != null) 88 | prev.next = draw; 89 | else 90 | this.draw = draw; 91 | 92 | draw.next = null; 93 | } 94 | 95 | public Draw getDraw() { 96 | return draw; 97 | } 98 | 99 | public Rule next() { 100 | return next; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/rules/RuleSet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.rules; 5 | 6 | import java.awt.Color; 7 | import java.io.FileInputStream; 8 | import java.io.FileNotFoundException; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.util.Map; 12 | 13 | import javax.xml.parsers.ParserConfigurationException; 14 | 15 | import io.vertx.workshop.map.MapException; 16 | import org.xml.sax.SAXException; 17 | 18 | import io.vertx.workshop.map.rules.osm.OSMRules; 19 | 20 | /** 21 | * Defines drawing rules for the renderer. This Object defines drawing rules for a Renderer. 22 | * 23 | * @author Paulo Lopes 24 | */ 25 | public class RuleSet extends OSMRules { 26 | 27 | private static final int TAG_CMP_NOT_EQUAL = 0; 28 | private static final int TAG_CMP_EQUAL = 1; 29 | private static final int TAG_CMP_ANY = 2; 30 | private static final int TAG_CMP_MISSING = 3; 31 | 32 | private final Rule root; 33 | private Color bgColor = Color.WHITE; 34 | 35 | public RuleSet(String filename) throws MapException, FileNotFoundException { 36 | this(new FileInputStream(filename)); 37 | } 38 | 39 | public RuleSet(InputStream stream) throws MapException { 40 | try { 41 | Rule o_root = null; 42 | Rule curr = null; 43 | Rule next; 44 | 45 | for (Rule r = read(stream); r != null; ) { 46 | next = r.next(); 47 | 48 | if (r.getDraw() != null) { 49 | if (o_root == null) { 50 | o_root = r; 51 | curr = o_root; 52 | } else { 53 | curr.addRule(r); 54 | curr = r; 55 | } 56 | } 57 | r = next; 58 | } 59 | root = o_root; 60 | } catch (SAXException | ParserConfigurationException | IOException e) { 61 | throw new MapException(e); 62 | } 63 | } 64 | 65 | @Override 66 | protected void setBgColor(Color bgColor) { 67 | this.bgColor = bgColor; 68 | } 69 | 70 | public Color getBgColor() { 71 | return bgColor; 72 | } 73 | 74 | public Rule root() { 75 | return root; 76 | } 77 | 78 | private static int stringInStrings(String string, String[] strings) { 79 | for (int i = 0; i < strings.length; i++) { 80 | if (string.equals(strings[i])) 81 | return TAG_CMP_EQUAL; 82 | if ("*".equals(strings[i])) 83 | return TAG_CMP_ANY; 84 | if ("~".equals(strings[i])) 85 | return TAG_CMP_MISSING; 86 | } 87 | return TAG_CMP_NOT_EQUAL; 88 | } 89 | 90 | /** 91 | * function: matchRule 92 | *

93 | * Check if an element matches a rule. 94 | */ 95 | public static boolean matchRule(Rule rule, Map tags) { 96 | int k, v; 97 | 98 | for (Map.Entry t : tags.entrySet()) { 99 | k = stringInStrings(t.getKey(), rule.keys); 100 | v = stringInStrings(t.getValue(), rule.values); 101 | 102 | if (k == TAG_CMP_EQUAL && v == TAG_CMP_EQUAL) 103 | return true; 104 | if (k == TAG_CMP_EQUAL && v == TAG_CMP_ANY) 105 | return true; 106 | if (k == TAG_CMP_NOT_EQUAL && v == TAG_CMP_MISSING) 107 | return true; 108 | } 109 | return false; 110 | } 111 | 112 | /** 113 | * function: checkRule 114 | *

115 | * Check if an element matches to a rule and to all it's parents. 116 | */ 117 | public static boolean checkRule(Rule rule, Map tags) { 118 | 119 | for (Rule r = rule.getParent(); r != null; r = r.getParent()) { 120 | if (matchRule(r, tags) == r.negate) { 121 | return false; 122 | } 123 | } 124 | 125 | return matchRule(rule, tags); 126 | 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/rules/osm/OSMRules.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.rules.osm; 5 | 6 | import java.awt.Color; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.util.logging.Logger; 10 | 11 | import javax.xml.parsers.ParserConfigurationException; 12 | import javax.xml.parsers.SAXParserFactory; 13 | 14 | import io.vertx.workshop.map.rules.Rule; 15 | import io.vertx.workshop.map.rules.Draw; 16 | import org.xml.sax.Attributes; 17 | import org.xml.sax.SAXException; 18 | import org.xml.sax.helpers.DefaultHandler; 19 | 20 | /** 21 | * LinkedList Struct for Rules 22 | * 23 | * @author Paulo Lopes 24 | */ 25 | public abstract class OSMRules extends DefaultHandler { 26 | 27 | private int depth; 28 | private Rule rule; 29 | 30 | // parsing helpers 31 | private static final Logger LOG = Logger.getLogger(OSMRules.class.getName()); 32 | private static final int MAXSTACK = 20; 33 | // Pointers to work with 34 | private Rule currentRule; 35 | private Rule[] ruleStack; 36 | 37 | public Rule read(InputStream stream) throws SAXException, IOException, ParserConfigurationException { 38 | 39 | depth = -1; 40 | rule = null; 41 | 42 | long tRead = System.currentTimeMillis(); 43 | 44 | // NULL rule stack 45 | ruleStack = new Rule[MAXSTACK]; 46 | 47 | // Create a builder factory 48 | SAXParserFactory factory = SAXParserFactory.newInstance(); 49 | factory.setValidating(false); 50 | // Create the builder and parse the file 51 | factory.newSAXParser().parse(stream, this); 52 | // free resources 53 | stream.close(); 54 | 55 | LOG.info("OSM Rules parsing done (" + ((System.currentTimeMillis() - tRead) / 1000f) + ") secs"); 56 | ruleStack = null; 57 | return rule; 58 | } 59 | 60 | @Override 61 | public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 62 | // Parsing Rules 63 | if ("rules".equals(qName)) { 64 | // Init Ruleset 65 | rule = null; 66 | String bg = attributes.getValue("background"); 67 | if (bg.length() > 7) { 68 | setBgColor(new Color( 69 | Integer.parseInt(bg.substring(1, 3), 16), 70 | Integer.parseInt(bg.substring(3, 5), 16), 71 | Integer.parseInt(bg.substring(5, 7), 16), 72 | Integer.parseInt(bg.substring(7, 9), 16))); 73 | } else { 74 | setBgColor(new Color( 75 | Integer.parseInt(bg.substring(1, 3), 16), 76 | Integer.parseInt(bg.substring(3, 5), 16), 77 | Integer.parseInt(bg.substring(5, 7), 16), 255)); 78 | } 79 | } 80 | // Parsing Rule 81 | else if ("rule".equals(qName)) { 82 | depth++; 83 | 84 | // Create Rule 85 | int type = Rule.UNKNOWN; 86 | 87 | // Populate Rule 88 | String e = attributes.getValue("e"); 89 | if (e != null && e.contains("way")) { 90 | type |= Rule.WAY; 91 | } 92 | if (e != null && e.contains("node")) { 93 | type |= Rule.NODE; 94 | } 95 | 96 | String[] key = null, value = null; 97 | 98 | String k = attributes.getValue("k"); 99 | if (k != null) { 100 | key = k.split("\\|"); 101 | } 102 | String v = attributes.getValue("v"); 103 | if (v != null) { 104 | value = v.split("\\|"); 105 | } 106 | if (key != null && value != null) { 107 | Rule new_rule = new Rule(type, key, value); 108 | 109 | // Insert Rule to chain 110 | if (rule == null) 111 | rule = new_rule; 112 | else 113 | currentRule.addRule(new_rule); 114 | currentRule = new_rule; 115 | 116 | // Adding to stack 117 | ruleStack[depth] = currentRule; 118 | } 119 | } 120 | // Parsing Else 121 | else if ("else".equals(qName)) { 122 | // the else rule is *always* after a rule, this means that the 123 | // current stack position 124 | // should contain the previous rule 125 | // Create Rule 126 | Rule new_rule = new Rule( 127 | ruleStack[depth].type, 128 | ruleStack[depth].keys, 129 | ruleStack[depth].values, 130 | true); 131 | 132 | depth++; 133 | 134 | // Insert Rule to chain (rule cannot be null) 135 | currentRule.addRule(new_rule); 136 | currentRule = new_rule; 137 | 138 | // Adding to stack 139 | ruleStack[depth] = currentRule; 140 | } 141 | // Parsing Polygone, etc. 142 | else if ("polygone".equals(qName) || "line".equals(qName) || "text".equals(qName)) { 143 | // Create Draw 144 | int type = Draw.UNKNOWN; 145 | 146 | // Populate Draw 147 | if ("polygone".equals(qName)) 148 | type = Draw.POLYGONE; 149 | else if ("line".equals(qName)) 150 | type = Draw.LINE; 151 | else if ("text".equals(qName)) 152 | type = Draw.TEXT; 153 | 154 | Draw draw = new Draw(type); 155 | 156 | String color = attributes.getValue("color"); 157 | if (color != null) { 158 | draw.setColor(new Color( 159 | Integer.parseInt(color.substring(1, 3), 16), 160 | Integer.parseInt(color.substring(3, 5), 16), 161 | Integer.parseInt(color.substring(5, 7), 16))); 162 | } 163 | String width = attributes.getValue("width"); 164 | if (width != null) { 165 | draw.setWidth(Float.parseFloat(width)); 166 | } 167 | draw.setPattern(attributes.getValue("pattern")); 168 | String zoom = attributes.getValue("zoom"); 169 | if (zoom != null) { 170 | String[] zooms = zoom.split(":"); 171 | draw.setZoom(Integer.parseInt(zooms[0]), Integer.parseInt(zooms[1])); 172 | } 173 | 174 | // Insert Draw 175 | ruleStack[depth].appendDraw(draw); 176 | } 177 | } 178 | 179 | @Override 180 | public void endElement(String uri, String localName, String qName) throws SAXException { 181 | if ("rule".equals(qName)) { 182 | // Fetching Parent from stack 183 | if (depth > 0) { 184 | ruleStack[depth].setParent(ruleStack[depth - 1]); 185 | } 186 | 187 | depth--; 188 | } else if ("else".equals(qName)) { 189 | // Fetching Parent from stack 190 | if (depth > 0) { 191 | ruleStack[depth].setParent(ruleStack[depth - 1]); 192 | } 193 | 194 | depth--; 195 | } 196 | } 197 | 198 | protected abstract void setBgColor(Color c); 199 | } 200 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/source/MapSource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.source; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.ObjectInputStream; 12 | import java.io.ObjectOutputStream; 13 | import java.util.List; 14 | import java.util.logging.Logger; 15 | import java.util.zip.GZIPInputStream; 16 | import java.util.zip.GZIPOutputStream; 17 | 18 | import io.vertx.workshop.map.BoundingBox; 19 | import io.vertx.workshop.map.MapException; 20 | import io.vertx.workshop.map.index.QTree; 21 | import io.vertx.workshop.map.source.osm.OSMReader; 22 | 23 | /** 24 | * Map Source is a class capable of loading some source map data into a structure we can use to render. 25 | * 26 | * @author Paulo Lopes 27 | */ 28 | public class MapSource extends OSMReader { 29 | 30 | private static final Logger LOG = Logger.getLogger(MapSource.class.getName()); 31 | 32 | private QTree wayIndex; 33 | 34 | @SuppressWarnings("unchecked") 35 | public MapSource(String filename) throws MapException, ClassNotFoundException, IOException { 36 | long tRead = System.currentTimeMillis(); 37 | File fIndex = new File(filename + ".idx.gz"); 38 | if (fIndex.exists()) { 39 | ObjectInputStream in = new ObjectInputStream(new FileInputStream(fIndex)); 40 | wayIndex = (QTree) in.readObject(); 41 | in.close(); 42 | } else { 43 | FileInputStream in = new FileInputStream(filename); 44 | load(in); 45 | in.close(); 46 | ObjectOutputStream out = new ObjectOutputStream(new GZIPOutputStream(new FileOutputStream(fIndex))); 47 | out.writeObject(wayIndex); 48 | out.close(); 49 | } 50 | LOG.info("OSM loading done (" + ((System.currentTimeMillis() - tRead) / 1000f) + ") secs"); 51 | } 52 | 53 | @SuppressWarnings("unchecked") 54 | public MapSource(InputStream in) throws ClassNotFoundException, IOException { 55 | long tRead = System.currentTimeMillis(); 56 | ObjectInputStream oin = new ObjectInputStream(new GZIPInputStream(in)); 57 | wayIndex = (QTree) oin.readObject(); 58 | oin.close(); 59 | LOG.info("OSM loading done (" + ((System.currentTimeMillis() - tRead) / 1000f) + ") secs"); 60 | } 61 | 62 | public BoundingBox getBoundingBox() { 63 | return wayIndex.getBoundingBox(); 64 | } 65 | 66 | public List getWaysInBoundingBox(BoundingBox bbox) { 67 | return wayIndex.get(bbox); 68 | } 69 | 70 | public List getNodesInBoundingBox(BoundingBox bbox) { 71 | throw new RuntimeException("Not implemented!"); 72 | } 73 | 74 | @Override 75 | public void initIndex(BoundingBox bbox) { 76 | wayIndex = new QTree<>(bbox); 77 | } 78 | 79 | @Override 80 | public void indexWay(Way w) { 81 | wayIndex.add(w); 82 | } 83 | 84 | @Override 85 | public void indexNode(Node n) { 86 | 87 | } 88 | 89 | // public static void main(String[] args) throws Exception { 90 | // new MapSource("map-render/map-data/Antwerpen.osm"); 91 | // } 92 | } 93 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/source/Member.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.source; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * OSM Member 10 | * 11 | * @author Paulo Lopes 12 | */ 13 | class Member implements Serializable { 14 | 15 | private static final long serialVersionUID = 1L; 16 | 17 | Node node; 18 | Way way; 19 | String role; 20 | Member next; 21 | } 22 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/source/Node.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.source; 5 | 6 | import java.io.Serializable; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * OSM Node 12 | * 13 | * @author Paulo Lopes 14 | */ 15 | public class Node implements Serializable { 16 | 17 | private static final long serialVersionUID = 1L; 18 | 19 | private final double lat; 20 | private final double lon; 21 | private int layer; 22 | 23 | private Map tags; 24 | 25 | public Node(double lat, double lon) { 26 | this.lat = lat; 27 | this.lon = lon; 28 | } 29 | 30 | public double getLat() { 31 | return lat; 32 | } 33 | 34 | public double getLon() { 35 | return lon; 36 | } 37 | 38 | public int getLayer() { 39 | return layer; 40 | } 41 | 42 | public void setLayer(int layer) { 43 | this.layer = layer; 44 | } 45 | 46 | public void insertTag(String key, String value) { 47 | 48 | if(tags == null) tags = new HashMap<>(); 49 | tags.put(key, value); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/source/Relation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.source; 5 | 6 | import java.io.Serializable; 7 | import java.util.Map; 8 | 9 | /** 10 | * OSM Relation 11 | * 12 | * @author Paulo Lopes 13 | */ 14 | class Relation implements Serializable { 15 | 16 | private static final long serialVersionUID = 1L; 17 | 18 | int id; 19 | Map tags; 20 | Member member; 21 | } 22 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/source/Way.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.source; 5 | 6 | import java.io.Serializable; 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import io.vertx.workshop.map.BoundingBox; 13 | 14 | /** 15 | * OSM Way 16 | * 17 | * @author Paulo Lopes 18 | */ 19 | public class Way extends BoundingBox implements Serializable { 20 | 21 | private static final long serialVersionUID = 1L; 22 | 23 | private final List nd; 24 | 25 | private int layer; 26 | private String name; 27 | private Map tags; 28 | 29 | public Way() { 30 | super(360.0, 360.0, -360.0, -360.0); 31 | nd = new ArrayList<>(); 32 | } 33 | 34 | public void setName(String name) { 35 | this.name = name; 36 | } 37 | 38 | public String getName() { 39 | return name; 40 | } 41 | 42 | public int getLayer() { 43 | return layer; 44 | } 45 | 46 | public void setLayer(int layer) { 47 | this.layer = layer; 48 | } 49 | 50 | public void insertTag(String key, String value) { 51 | if (tags == null) tags = new HashMap<>(); 52 | tags.put(key, value); 53 | } 54 | 55 | public void addWayNode(Node node) { 56 | nd.add(node); 57 | double lat = node.getLat(); 58 | double lon = node.getLon(); 59 | 60 | if (lat < getMinLat()) setMinLat(lat); 61 | if (lat > getMaxLat()) setMaxLat(lat); 62 | if (lon < getMinLon()) setMinLon(lon); 63 | if (lon > getMaxLon()) setMaxLon(lon); 64 | } 65 | 66 | public Map getTags() { 67 | return tags; 68 | } 69 | 70 | public List getWayNode() { 71 | return nd; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /map-render-service/src/main/java/io/vertx/workshop/map/source/osm/OSMReader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.source.osm; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.logging.Logger; 11 | 12 | import javax.xml.parsers.ParserConfigurationException; 13 | import javax.xml.parsers.SAXParserFactory; 14 | 15 | import io.vertx.workshop.map.source.Node; 16 | import io.vertx.workshop.map.source.Way; 17 | import io.vertx.workshop.map.MapException; 18 | import org.xml.sax.Attributes; 19 | import org.xml.sax.SAXException; 20 | import org.xml.sax.helpers.DefaultHandler; 21 | 22 | import io.vertx.workshop.map.BoundingBox; 23 | 24 | /** 25 | * OSM XML Parser for the OSM format 26 | * 27 | * @author Paulo Lopes 28 | */ 29 | public abstract class OSMReader extends DefaultHandler { 30 | 31 | Map nodeidx = new HashMap<>(); 32 | 33 | private Node cNode = null; 34 | private Way cWay = null; 35 | 36 | private static final Logger LOG = Logger.getLogger(OSMReader.class.getName()); 37 | 38 | public void load(InputStream stream) throws MapException { 39 | try { 40 | // Create a builder factory 41 | SAXParserFactory factory = SAXParserFactory.newInstance(); 42 | factory.setValidating(false); 43 | // Create the builder and parse the file 44 | factory.newSAXParser().parse(stream, this); 45 | 46 | nodeidx.clear(); 47 | nodeidx = null; 48 | } catch(SAXException | IOException | ParserConfigurationException e) { 49 | throw new MapException(e); 50 | } 51 | } 52 | 53 | @Override 54 | @SuppressWarnings("boxing") 55 | public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 56 | // Parsing Bounds 57 | if ("bounds".equals(qName)) { 58 | // LOG.fine("Parsing bounds"); 59 | initIndex(new BoundingBox( 60 | Double.parseDouble(attributes.getValue("minlat")), 61 | Double.parseDouble(attributes.getValue("minlon")), 62 | Double.parseDouble(attributes.getValue("maxlat")), 63 | Double.parseDouble(attributes.getValue("maxlon")))); 64 | } 65 | // Parsing bound (OSM 0.6) using osmosis 66 | if ("bound".equals(qName)) { 67 | String[] box = attributes.getValue("box").split(","); 68 | initIndex(new BoundingBox( 69 | Double.parseDouble(box[0]), 70 | Double.parseDouble(box[1]), 71 | Double.parseDouble(box[2]), 72 | Double.parseDouble(box[3]))); 73 | } 74 | // Parsing Node 75 | else if ("node".equals(qName)) { 76 | long id = Long.parseLong(attributes.getValue("id")); 77 | cNode = new Node( 78 | Double.parseDouble(attributes.getValue("lat")), 79 | Double.parseDouble(attributes.getValue("lon"))); 80 | 81 | // Insert Node local hash 82 | nodeidx.put(id, cNode); 83 | } 84 | // Parsing Tags 85 | else if ("tag".equals(qName)) { 86 | 87 | if (cNode == null && cWay == null) // End if there is nothing to add the tag to 88 | return; 89 | 90 | String k, v; 91 | 92 | k = attributes.getValue("k").intern(); 93 | v = attributes.getValue("v").intern(); 94 | // attributes.getValue("created_by"); 95 | // attributes.getValue("source"); 96 | if ("layer".equals(k)) { 97 | int layer; 98 | try { 99 | if(v.charAt(0) == '+') v = v.substring(1); 100 | layer = Integer.parseInt(v); 101 | } catch(NumberFormatException nfe) { 102 | LOG.severe("Not a number: " + v); 103 | layer = 1; 104 | } 105 | if (cNode != null) { 106 | cNode.setLayer(layer); 107 | } else { 108 | cWay.setLayer(layer); 109 | } 110 | } else if ("name".equals(k)) { 111 | if (cWay != null) { 112 | cWay.setName(v); 113 | } 114 | } 115 | 116 | if (cNode != null) 117 | cNode.insertTag(k, v); 118 | else if (cWay != null) 119 | cWay.insertTag(k, v); 120 | } 121 | // Parsing Way 122 | else if ("way".equals(qName)) { 123 | cWay = new Way(); 124 | } 125 | // Parsing WayNode 126 | else if ("nd".equals(qName)) { 127 | long ref = Long.parseLong(attributes.getValue("ref")); 128 | 129 | if (ref != 0) { 130 | Node n; 131 | 132 | n = nodeidx.get(ref); 133 | if (n == null) { 134 | LOG.severe("No node with reference " + ref + " found!"); 135 | return; 136 | } 137 | 138 | // Insert WayNode 139 | cWay.addWayNode(n); 140 | cNode = null; 141 | } 142 | } 143 | } 144 | 145 | @Override 146 | public void endElement(String uri, String localName, String qName) throws SAXException { 147 | if ("node".equals(qName)) { 148 | if(cNode != null) { 149 | indexNode(cNode); 150 | } 151 | cNode = null; 152 | } else if ("way".equals(qName)) { 153 | if(cWay != null) { 154 | indexWay(cWay); 155 | } 156 | cWay = null; 157 | } 158 | } 159 | 160 | public abstract void initIndex(BoundingBox bbox); 161 | 162 | public abstract void indexWay(Way w); 163 | 164 | public abstract void indexNode(Node n); 165 | } 166 | -------------------------------------------------------------------------------- /map-render-service/src/main/resources/antwerpen.osm.idx.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cescoffier/vertx-workshop/e1d8d12970bbca89c74927843035d836f204be9f/map-render-service/src/main/resources/antwerpen.osm.idx.gz -------------------------------------------------------------------------------- /map-render-service/src/main/resources/pattern/landuse-cemetery-christian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cescoffier/vertx-workshop/e1d8d12970bbca89c74927843035d836f204be9f/map-render-service/src/main/resources/pattern/landuse-cemetery-christian.png -------------------------------------------------------------------------------- /map-render-service/src/main/resources/pattern/landuse-cemetery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cescoffier/vertx-workshop/e1d8d12970bbca89c74927843035d836f204be9f/map-render-service/src/main/resources/pattern/landuse-cemetery.png -------------------------------------------------------------------------------- /map-render-service/src/main/resources/pattern/landuse-vineyard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cescoffier/vertx-workshop/e1d8d12970bbca89c74927843035d836f204be9f/map-render-service/src/main/resources/pattern/landuse-vineyard.png -------------------------------------------------------------------------------- /map-render-service/src/main/resources/pattern/military.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cescoffier/vertx-workshop/e1d8d12970bbca89c74927843035d836f204be9f/map-render-service/src/main/resources/pattern/military.png -------------------------------------------------------------------------------- /map-render-service/src/main/resources/pattern/wood-deciduous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cescoffier/vertx-workshop/e1d8d12970bbca89c74927843035d836f204be9f/map-render-service/src/main/resources/pattern/wood-deciduous.png -------------------------------------------------------------------------------- /map-render-service/src/main/resources/pattern/wood-mixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cescoffier/vertx-workshop/e1d8d12970bbca89c74927843035d836f204be9f/map-render-service/src/main/resources/pattern/wood-mixed.png -------------------------------------------------------------------------------- /map-render-service/src/main/resources/rule.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /map-server-proxy/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | 8 | 9 | io.vertx.workshop 10 | vertx-microservice-workshop 11 | 1.0-SNAPSHOT 12 | 13 | 14 | map-server-proxy 15 | Map Render Service - Proxy 16 | 17 | 18 | io.vertx.workshop.map.server.MapServerVerticle 19 | 20 | 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-shade-plugin 26 | 27 | 28 | org.jolokia 29 | docker-maven-plugin 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /map-server-proxy/src/main/docker/assembly.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | target/${project.artifactId}-${project.version}-fat.jar 7 | . 8 | vertx-service-fat.jar 9 | 10 | 11 | 12 | 13 | 14 | ${project.basedir}/src/conf 15 | 16 | *.json 17 | 18 | . 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /map-server-proxy/src/main/java/io/vertx/workshop/map/server/MapServerVerticle.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 the original author or authors. 3 | */ 4 | package io.vertx.workshop.map.server; 5 | 6 | import io.vertx.core.AbstractVerticle; 7 | import io.vertx.core.buffer.Buffer; 8 | import io.vertx.core.eventbus.DeliveryOptions; 9 | import io.vertx.core.eventbus.EventBus; 10 | import io.vertx.core.http.HttpMethod; 11 | import io.vertx.core.http.HttpServerRequest; 12 | 13 | import java.text.ParseException; 14 | import java.text.SimpleDateFormat; 15 | import java.util.Date; 16 | import java.util.TimeZone; 17 | import java.util.regex.Matcher; 18 | import java.util.regex.Pattern; 19 | 20 | /** 21 | * Map Tile Server Micro-Service 22 | * 23 | * @author Paulo Lopes 24 | */ 25 | public class MapServerVerticle extends AbstractVerticle { 26 | 27 | // Regular Expression to match requests 28 | private static final Pattern TILE = Pattern.compile("^/(\\d+)/(\\d+)/(\\d+)\\.png$"); 29 | 30 | private final SimpleDateFormat ISODATE; 31 | 32 | public MapServerVerticle() { 33 | ISODATE = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz"); 34 | ISODATE.setTimeZone(TimeZone.getTimeZone("UTC")); 35 | } 36 | 37 | @Override 38 | public void start() { 39 | 40 | final int port = config().getInteger("port", 8000); 41 | final int cache = config().getInteger("cache", 86400); 42 | 43 | vertx.createHttpServer().requestHandler(req -> { 44 | if (req.method() == HttpMethod.GET) { 45 | final Matcher matcher = TILE.matcher(req.path()); 46 | 47 | if (matcher.matches()) { 48 | String z = matcher.group(1); 49 | String x = matcher.group(2); 50 | String y = matcher.group(3); 51 | 52 | final String etag = "\"" + z + "-" + x + "-" + y + "\""; 53 | 54 | req.response() 55 | .putHeader("etag", etag) 56 | .putHeader("cache-control", "public, max-age=" + cache); 57 | 58 | if (isFresh(etag, req)) { 59 | 60 | // some caching code here 61 | req.response() 62 | .setStatusCode(304) 63 | .end(); 64 | 65 | } else { 66 | //Replace next line by "vertx.createHttpClient().getNow(8001, "localhost"," to use the local version: 67 | vertx.createHttpClient().getNow(80, "map0render0service-vertxdemos.rhcloud.com", 68 | "/render/" + x + "/" + y + "/" + z, response -> { 69 | if (response.statusCode() == 200) { 70 | response.bodyHandler(buffer -> { 71 | req.response() 72 | .putHeader("last-modified", ISODATE.format(new Date())) 73 | .putHeader("content-type", "image/png") 74 | .end(buffer); 75 | } 76 | ); 77 | } else { 78 | req.response().setStatusCode(500).end("Cannot retrieve map tile"); 79 | } 80 | }); 81 | } 82 | return; 83 | } 84 | } 85 | // if it reached this step it is a bad request 86 | req.response().setStatusCode(400).end(); 87 | 88 | }).listen(port); 89 | } 90 | 91 | private boolean isFresh(final String etag, final HttpServerRequest request) { 92 | // defaults 93 | boolean etagMatches = true; 94 | boolean notModified = true; 95 | 96 | // fields 97 | String modifiedSince = request.getHeader("if-modified-since"); 98 | String noneMatch = request.getHeader("if-none-match"); 99 | String[] noneMatchTokens = null; 100 | 101 | // unconditional request 102 | if (modifiedSince == null && noneMatch == null) { 103 | return false; 104 | } 105 | 106 | // parse if-none-match 107 | if (noneMatch != null) { 108 | noneMatchTokens = noneMatch.split(" *, *"); 109 | } 110 | 111 | // if-none-match 112 | if (noneMatchTokens != null) { 113 | etagMatches = false; 114 | for (String s : noneMatchTokens) { 115 | if (etag.equals(s) || "*".equals(noneMatchTokens[0])) { 116 | etagMatches = true; 117 | break; 118 | } 119 | } 120 | } 121 | 122 | // if-modified-since 123 | if (modifiedSince != null) { 124 | try { 125 | Date modifiedSinceDate = ISODATE.parse(modifiedSince); 126 | notModified = System.currentTimeMillis() <= modifiedSinceDate.getTime(); 127 | } catch (ParseException e) { 128 | notModified = false; 129 | } 130 | } 131 | 132 | return etagMatches && notModified; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.vertx.workshop 8 | vertx-microservice-workshop 9 | 1.0-SNAPSHOT 10 | 11 | pom 12 | 13 | 14 | 3.1.0 15 | 16 | 17 | 18 | 19 | 20 | data-provisioning 21 | data-storage-service 22 | data-storage-client 23 | recommendation-service 24 | map-render-service 25 | map-server-proxy 26 | frontend 27 | eventbus-to-hawkular-bridge 28 | 29 | 30 | 31 | 32 | 33 | io.vertx 34 | vertx-dependencies 35 | ${vertx.version} 36 | pom 37 | import 38 | 39 | 40 | 41 | biz.paluch.logging 42 | logstash-gelf 43 | 1.7.0 44 | 45 | 46 | junit 47 | junit 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | io.vertx 58 | vertx-core 59 | 60 | 61 | io.vertx 62 | vertx-hazelcast 63 | 64 | 65 | io.vertx 66 | vertx-codegen 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.apache.maven.plugins 75 | maven-shade-plugin 76 | 2.3 77 | 78 | 79 | package 80 | 81 | shade 82 | 83 | 84 | 85 | 86 | 87 | io.vertx.core.Launcher 88 | ${main.verticle} 89 | 90 | 91 | 92 | META-INF/services/io.vertx.core.spi.VerticleFactory 93 | 94 | 95 | 96 | 97 | ${project.build.directory}/${project.artifactId}-${project.version}-fat.jar 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.jolokia 105 | docker-maven-plugin 106 | 0.13.6 107 | 108 | 109 | 110 | vertx-devoxx/${project.artifactId}:${project.version} 111 | 112 | java:openjdk-8u66-jre 113 | 114 | latest 115 | 116 | 117 | 118 | java 119 | -jar 120 | /opt/vertx/vertx-service-fat.jar 121 | --conf=/opt/vertx/config.json 122 | --cluster 123 | 124 | 125 | 126 | dir 127 | /opt/vertx 128 | 129 | assembly.xml 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | maven-resources-plugin 139 | 2.7 140 | 141 | 142 | add-groovy-sources 143 | prepare-package 144 | 145 | copy-resources 146 | 147 | 148 | ${project.build.outputDirectory} 149 | 150 | 151 | ${project.basedir}/src/main/groovy 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | maven-compiler-plugin 163 | 3.1 164 | 165 | 1.8 166 | 1.8 167 | 168 | io.vertx.codegen.CodeGenProcessor 169 | 170 | 171 | -AoutputDirectory=${project.basedir}/src/main 172 | 173 | ${project.basedir}/src/main/generated 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /recommendation-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | io.vertx.workshop 9 | vertx-microservice-workshop 10 | 1.0-SNAPSHOT 11 | 12 | 13 | recommendation-service 14 | Recommendation Service 15 | 16 | 17 | io.vertx.workshop.recommendation.impl.RecommendationVerticle 18 | 19 | 20 | 21 | 22 | io.vertx 23 | vertx-redis-client 24 | 25 | 26 | io.vertx 27 | vertx-service-proxy 28 | 29 | 30 | io.vertx 31 | vertx-lang-groovy 32 | 33 | 34 | io.vertx 35 | vertx-lang-js 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-shade-plugin 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /recommendation-service/src/conf/config-docker-machine.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "192.168.99.100", 3 | "port": 6379 4 | } -------------------------------------------------------------------------------- /recommendation-service/src/conf/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "localhost", 3 | "port": 6379 4 | } -------------------------------------------------------------------------------- /recommendation-service/src/main/generated/io/vertx/workshop/recommendation/RecommendationServiceVertxEBProxy.java: -------------------------------------------------------------------------------- 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 | package io.vertx.workshop.recommendation; 18 | 19 | import io.vertx.workshop.recommendation.RecommendationService; 20 | import io.vertx.core.eventbus.DeliveryOptions; 21 | import io.vertx.core.Vertx; 22 | import io.vertx.core.Future; 23 | import io.vertx.core.json.JsonObject; 24 | import io.vertx.core.json.JsonArray; 25 | import java.util.ArrayList; 26 | import java.util.HashSet; 27 | import java.util.List; 28 | import java.util.Map; 29 | import java.util.Set; 30 | import java.util.stream.Collectors; 31 | import io.vertx.serviceproxy.ProxyHelper; 32 | import io.vertx.core.Vertx; 33 | import io.vertx.core.json.JsonObject; 34 | import io.vertx.core.AsyncResult; 35 | import io.vertx.core.Handler; 36 | import io.vertx.workshop.recommendation.RecommendationService; 37 | 38 | /* 39 | Generated Proxy code - DO NOT EDIT 40 | @author Roger the Robot 41 | */ 42 | public class RecommendationServiceVertxEBProxy implements RecommendationService { 43 | 44 | private Vertx _vertx; 45 | private String _address; 46 | private DeliveryOptions _options; 47 | private boolean closed; 48 | 49 | public RecommendationServiceVertxEBProxy(Vertx vertx, String address) { 50 | this(vertx, address, null); 51 | } 52 | 53 | public RecommendationServiceVertxEBProxy(Vertx vertx, String address, DeliveryOptions options) { 54 | this._vertx = vertx; 55 | this._address = address; 56 | this._options = options; 57 | } 58 | 59 | public void vote(String name, boolean plus, Handler> resultHandler) { 60 | if (closed) { 61 | resultHandler.handle(Future.failedFuture(new IllegalStateException("Proxy is closed"))); 62 | return; 63 | } 64 | JsonObject _json = new JsonObject(); 65 | _json.put("name", name); 66 | _json.put("plus", plus); 67 | DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions(); 68 | _deliveryOptions.addHeader("action", "vote"); 69 | _vertx.eventBus().send(_address, _json, _deliveryOptions, res -> { 70 | if (res.failed()) { 71 | resultHandler.handle(Future.failedFuture(res.cause())); 72 | } else { 73 | resultHandler.handle(Future.succeededFuture(res.result().body())); 74 | } 75 | }); 76 | } 77 | 78 | public void get(String name, Handler> resultHandler) { 79 | if (closed) { 80 | resultHandler.handle(Future.failedFuture(new IllegalStateException("Proxy is closed"))); 81 | return; 82 | } 83 | JsonObject _json = new JsonObject(); 84 | _json.put("name", name); 85 | DeliveryOptions _deliveryOptions = (_options != null) ? new DeliveryOptions(_options) : new DeliveryOptions(); 86 | _deliveryOptions.addHeader("action", "get"); 87 | _vertx.eventBus().send(_address, _json, _deliveryOptions, res -> { 88 | if (res.failed()) { 89 | resultHandler.handle(Future.failedFuture(res.cause())); 90 | } else { 91 | resultHandler.handle(Future.succeededFuture(res.result().body())); 92 | } 93 | }); 94 | } 95 | 96 | 97 | private List convertToListChar(JsonArray arr) { 98 | List list = new ArrayList<>(); 99 | for (Object obj: arr) { 100 | Integer jobj = (Integer)obj; 101 | list.add((char)(int)jobj); 102 | } 103 | return list; 104 | } 105 | 106 | private Set convertToSetChar(JsonArray arr) { 107 | Set set = new HashSet<>(); 108 | for (Object obj: arr) { 109 | Integer jobj = (Integer)obj; 110 | set.add((char)(int)jobj); 111 | } 112 | return set; 113 | } 114 | 115 | private Map convertMap(Map map) { 116 | return (Map)map; 117 | } 118 | private List convertList(List list) { 119 | return (List)list; 120 | } 121 | private Set convertSet(List list) { 122 | return new HashSet((List)list); 123 | } 124 | } -------------------------------------------------------------------------------- /recommendation-service/src/main/generated/io/vertx/workshop/recommendation/RecommendationServiceVertxProxyHandler.java: -------------------------------------------------------------------------------- 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 | package io.vertx.workshop.recommendation; 18 | 19 | import io.vertx.workshop.recommendation.RecommendationService; 20 | import io.vertx.core.Vertx; 21 | import io.vertx.core.Handler; 22 | import io.vertx.core.AsyncResult; 23 | import io.vertx.core.eventbus.EventBus; 24 | import io.vertx.core.eventbus.Message; 25 | import io.vertx.core.eventbus.MessageConsumer; 26 | import io.vertx.core.eventbus.DeliveryOptions; 27 | import io.vertx.core.eventbus.ReplyException; 28 | import io.vertx.core.json.JsonObject; 29 | import io.vertx.core.json.JsonArray; 30 | import java.util.Collection; 31 | import java.util.ArrayList; 32 | import java.util.HashSet; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.Set; 36 | import java.util.UUID; 37 | import java.util.stream.Collectors; 38 | import io.vertx.serviceproxy.ProxyHelper; 39 | import io.vertx.serviceproxy.ProxyHandler; 40 | import io.vertx.core.Vertx; 41 | import io.vertx.core.json.JsonObject; 42 | import io.vertx.core.AsyncResult; 43 | import io.vertx.core.Handler; 44 | import io.vertx.workshop.recommendation.RecommendationService; 45 | 46 | /* 47 | Generated Proxy code - DO NOT EDIT 48 | @author Roger the Robot 49 | */ 50 | public class RecommendationServiceVertxProxyHandler extends ProxyHandler { 51 | 52 | public static final long DEFAULT_CONNECTION_TIMEOUT = 5 * 60; // 5 minutes 53 | 54 | private final Vertx vertx; 55 | private final RecommendationService service; 56 | private final long timerID; 57 | private long lastAccessed; 58 | private final long timeoutSeconds; 59 | 60 | public RecommendationServiceVertxProxyHandler(Vertx vertx, RecommendationService service) { 61 | this(vertx, service, DEFAULT_CONNECTION_TIMEOUT); 62 | } 63 | 64 | public RecommendationServiceVertxProxyHandler(Vertx vertx, RecommendationService service, long timeoutInSecond) { 65 | this(vertx, service, true, timeoutInSecond); 66 | } 67 | 68 | public RecommendationServiceVertxProxyHandler(Vertx vertx, RecommendationService service, boolean topLevel, long timeoutSeconds) { 69 | this.vertx = vertx; 70 | this.service = service; 71 | this.timeoutSeconds = timeoutSeconds; 72 | if (timeoutSeconds != -1 && !topLevel) { 73 | long period = timeoutSeconds * 1000 / 2; 74 | if (period > 10000) { 75 | period = 10000; 76 | } 77 | this.timerID = vertx.setPeriodic(period, this::checkTimedOut); 78 | } else { 79 | this.timerID = -1; 80 | } 81 | accessed(); 82 | } 83 | 84 | public MessageConsumer registerHandler(String address) { 85 | MessageConsumer consumer = vertx.eventBus().consumer(address).handler(this); 86 | this.setConsumer(consumer); 87 | return consumer; 88 | } 89 | 90 | private void checkTimedOut(long id) { 91 | long now = System.nanoTime(); 92 | if (now - lastAccessed > timeoutSeconds * 1000000000) { 93 | close(); 94 | } 95 | } 96 | 97 | @Override 98 | public void close() { 99 | if (timerID != -1) { 100 | vertx.cancelTimer(timerID); 101 | } 102 | super.close(); 103 | } 104 | 105 | private void accessed() { 106 | this.lastAccessed = System.nanoTime(); 107 | } 108 | 109 | public void handle(Message msg) { 110 | try { 111 | JsonObject json = msg.body(); 112 | String action = msg.headers().get("action"); 113 | if (action == null) { 114 | throw new IllegalStateException("action not specified"); 115 | } 116 | accessed(); 117 | switch (action) { 118 | 119 | case "vote": { 120 | service.vote((java.lang.String)json.getValue("name"), (boolean)json.getValue("plus"), createHandler(msg)); 121 | break; 122 | } 123 | case "get": { 124 | service.get((java.lang.String)json.getValue("name"), createHandler(msg)); 125 | break; 126 | } 127 | default: { 128 | throw new IllegalStateException("Invalid action: " + action); 129 | } 130 | } 131 | } catch (Throwable t) { 132 | msg.fail(-1, t.getMessage()); 133 | throw t; 134 | } 135 | } 136 | 137 | private Handler> createHandler(Message msg) { 138 | return res -> { 139 | if (res.failed()) { 140 | msg.fail(-1, res.cause().getMessage()); 141 | } else { 142 | msg.reply(res.result()); 143 | } 144 | }; 145 | } 146 | 147 | private Handler>> createListHandler(Message msg) { 148 | return res -> { 149 | if (res.failed()) { 150 | msg.fail(-1, res.cause().getMessage()); 151 | } else { 152 | msg.reply(new JsonArray(res.result())); 153 | } 154 | }; 155 | } 156 | 157 | private Handler>> createSetHandler(Message msg) { 158 | return res -> { 159 | if (res.failed()) { 160 | msg.fail(-1, res.cause().getMessage()); 161 | } else { 162 | msg.reply(new JsonArray(new ArrayList<>(res.result()))); 163 | } 164 | }; 165 | } 166 | 167 | private Handler>> createListCharHandler(Message msg) { 168 | return res -> { 169 | if (res.failed()) { 170 | msg.fail(-1, res.cause().getMessage()); 171 | } else { 172 | JsonArray arr = new JsonArray(); 173 | for (Character chr: res.result()) { 174 | arr.add((int) chr); 175 | } 176 | msg.reply(arr); 177 | } 178 | }; 179 | } 180 | 181 | private Handler>> createSetCharHandler(Message msg) { 182 | return res -> { 183 | if (res.failed()) { 184 | msg.fail(-1, res.cause().getMessage()); 185 | } else { 186 | JsonArray arr = new JsonArray(); 187 | for (Character chr: res.result()) { 188 | arr.add((int) chr); 189 | } 190 | msg.reply(arr); 191 | } 192 | }; 193 | } 194 | 195 | private Map convertMap(Map map) { 196 | return (Map)map; 197 | } 198 | 199 | private List convertList(List list) { 200 | return (List)list; 201 | } 202 | 203 | private Set convertSet(List list) { 204 | return new HashSet((List)list); 205 | } 206 | } -------------------------------------------------------------------------------- /recommendation-service/src/main/groovy/io/vertx/workshop/groovy/recommendation/RecommendationService.groovy: -------------------------------------------------------------------------------- 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 | package io.vertx.workshop.groovy.recommendation; 18 | import groovy.transform.CompileStatic 19 | import io.vertx.lang.groovy.InternalHelper 20 | import io.vertx.core.json.JsonObject 21 | import io.vertx.groovy.core.Vertx 22 | import io.vertx.core.json.JsonObject 23 | import io.vertx.core.AsyncResult 24 | import io.vertx.core.Handler 25 | /** 26 | * A service to recommend places. 27 | */ 28 | @CompileStatic 29 | public class RecommendationService { 30 | private final def io.vertx.workshop.recommendation.RecommendationService delegate; 31 | public RecommendationService(Object delegate) { 32 | this.delegate = (io.vertx.workshop.recommendation.RecommendationService) delegate; 33 | } 34 | public Object getDelegate() { 35 | return delegate; 36 | } 37 | /** 38 | * The method used to create proxy to consume the service. 39 | * @param vertx vert.x 40 | * @param address the address where the service is served 41 | * @return the proxy 42 | */ 43 | public static RecommendationService createProxy(Vertx vertx, String address) { 44 | def ret= InternalHelper.safeCreate(io.vertx.workshop.recommendation.RecommendationService.createProxy((io.vertx.core.Vertx)vertx.getDelegate(), address), io.vertx.workshop.groovy.recommendation.RecommendationService.class); 45 | return ret; 46 | } 47 | /** 48 | * Votes for a place. 49 | * When a vote is placed, the new rating of the place are brodcasted to the event bus. 50 | * @param name the name of the place 51 | * @param plus whether or not you liked the place. 52 | * @param resultHandler invoked when the action is completed 53 | */ 54 | public void vote(String name, boolean plus, Handler> resultHandler) { 55 | this.delegate.vote(name, plus, resultHandler); 56 | } 57 | /** 58 | * Gets the number or votes. 59 | * @param name the name of the place 60 | * @param resultHandler the result handler. The votes are given in a JSON object using the "up" and "down" fields. 61 | */ 62 | public void get(String name, Handler>> resultHandler) { 63 | this.delegate.get(name, new Handler>() { 64 | public void handle(AsyncResult event) { 65 | AsyncResult> f 66 | if (event.succeeded()) { 67 | f = InternalHelper.>result((Map)InternalHelper.wrapObject(event.result())) 68 | } else { 69 | f = InternalHelper.>failure(event.cause()) 70 | } 71 | resultHandler.handle(f) 72 | } 73 | }); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /recommendation-service/src/main/java/io/vertx/workshop/recommendation/RecommendationService.java: -------------------------------------------------------------------------------- 1 | package io.vertx.workshop.recommendation; 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.Vertx; 8 | import io.vertx.core.json.JsonObject; 9 | import io.vertx.serviceproxy.ProxyHelper; 10 | 11 | /** 12 | * A service to recommend places. 13 | */ 14 | @VertxGen 15 | @ProxyGen 16 | public interface RecommendationService { 17 | 18 | /** 19 | * The method used to create proxy to consume the service. 20 | * 21 | * @param vertx vert.x 22 | * @param address the address where the service is served 23 | * @return the proxy 24 | */ 25 | static RecommendationService createProxy(Vertx vertx, String address) { 26 | return ProxyHelper.createProxy(RecommendationService.class, vertx, address); 27 | } 28 | 29 | /** 30 | * Votes for a place. 31 | * When a vote is placed, the new rating of the place are brodcasted to the event bus. 32 | * 33 | * @param name the name of the place 34 | * @param plus whether or not you liked the place. 35 | * @param resultHandler invoked when the action is completed 36 | */ 37 | void vote(String name, boolean plus, Handler> resultHandler); 38 | 39 | /** 40 | * Gets the number or votes. 41 | * @param name the name of the place 42 | * @param resultHandler the result handler. The votes are given in a JSON object using the "up" and "down" fields. 43 | */ 44 | void get(String name, Handler> resultHandler); 45 | } 46 | -------------------------------------------------------------------------------- /recommendation-service/src/main/java/io/vertx/workshop/recommendation/impl/RecommendationServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.vertx.workshop.recommendation.impl; 2 | 3 | import io.vertx.core.AsyncResult; 4 | import io.vertx.core.Handler; 5 | import io.vertx.core.Vertx; 6 | import io.vertx.core.eventbus.EventBus; 7 | import io.vertx.core.json.JsonObject; 8 | import io.vertx.redis.RedisClient; 9 | import io.vertx.redis.RedisOptions; 10 | import io.vertx.workshop.recommendation.RecommendationService; 11 | 12 | public class RecommendationServiceImpl implements RecommendationService { 13 | 14 | // TODO Change to your own. 15 | public static final String RECOMMENDATIONS_ADDRESS = "devoxx.recommendations.announce"; 16 | private final RedisClient redis; 17 | private final EventBus eventBus; 18 | 19 | public RecommendationServiceImpl(Vertx vertx, JsonObject config) { 20 | this.eventBus = vertx.eventBus(); 21 | this.redis = RedisClient.create(vertx, new RedisOptions(config)); 22 | } 23 | 24 | @Override 25 | public void vote(String name, boolean plus, Handler> handler) { 26 | redis.hincrby(name, plus ? "up" : "down", 1, hincrby -> { 27 | // TODO to implement 28 | // TODO don't forget to publish the new ratings to RECOMMENDATIONS_ADDRESS 29 | }); 30 | } 31 | 32 | @Override 33 | public void get(String name, Handler> handler) { 34 | redis.hgetall(name, hgetall -> { 35 | // TODO to implement 36 | }); 37 | } 38 | 39 | public void close() { 40 | redis.close(v -> { 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /recommendation-service/src/main/java/io/vertx/workshop/recommendation/impl/RecommendationVerticle.java: -------------------------------------------------------------------------------- 1 | package io.vertx.workshop.recommendation.impl; 2 | 3 | import io.vertx.core.AbstractVerticle; 4 | import io.vertx.serviceproxy.ProxyHelper; 5 | import io.vertx.workshop.recommendation.RecommendationService; 6 | 7 | public class RecommendationVerticle extends AbstractVerticle { 8 | 9 | private RecommendationServiceImpl service; 10 | 11 | @Override 12 | public void start() throws Exception { 13 | service = new RecommendationServiceImpl(vertx, config()); 14 | //TODO Change address to your own. 15 | ProxyHelper.registerService(RecommendationService.class, vertx, service, "devoxx.recommendations"); 16 | } 17 | 18 | @Override 19 | public void stop() throws Exception { 20 | service.close(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /recommendation-service/src/main/java/io/vertx/workshop/recommendation/package-info.java: -------------------------------------------------------------------------------- 1 | 2 | @ModuleGen(name="vertx-microservice-workshop", groupPackage = "io.vertx.workshop") 3 | package io.vertx.workshop.recommendation; 4 | 5 | import io.vertx.codegen.annotations.ModuleGen; -------------------------------------------------------------------------------- /recommendation-service/src/main/resources/vertx-microservice-workshop-js/recommendation_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 | /** @module vertx-microservice-workshop-js/recommendation_service */ 18 | !function (factory) { 19 | if (typeof require === 'function' && typeof module !== 'undefined') { 20 | factory(); 21 | } else if (typeof define === 'function' && define.amd) { 22 | // AMD loader 23 | define('vertx-microservice-workshop-js/recommendation_service-proxy', [], factory); 24 | } else { 25 | // plain old include 26 | RecommendationService = factory(); 27 | } 28 | }(function () { 29 | 30 | /** 31 | A service to recommend places. 32 | 33 | @class 34 | */ 35 | var RecommendationService = function(eb, address) { 36 | 37 | var j_eb = eb; 38 | var j_address = address; 39 | var closed = false; 40 | var that = this; 41 | var convCharCollection = function(coll) { 42 | var ret = []; 43 | for (var i = 0;i < coll.length;i++) { 44 | ret.push(String.fromCharCode(coll[i])); 45 | } 46 | return ret; 47 | }; 48 | 49 | /** 50 | Votes for a place. 51 | When a vote is placed, the new rating of the place are brodcasted to the event bus. 52 | 53 | @public 54 | @param name {string} the name of the place 55 | @param plus {boolean} whether or not you liked the place. 56 | @param resultHandler {function} invoked when the action is completed 57 | */ 58 | this.vote = function(name, plus, resultHandler) { 59 | var __args = arguments; 60 | if (__args.length === 3 && typeof __args[0] === 'string' && typeof __args[1] ==='boolean' && typeof __args[2] === 'function') { 61 | if (closed) { 62 | throw new Error('Proxy is closed'); 63 | } 64 | j_eb.send(j_address, {"name":__args[0], "plus":__args[1]}, {"action":"vote"}, function(err, result) { __args[2](err, result &&result.body); }); 65 | return; 66 | } else throw new TypeError('function invoked with invalid arguments'); 67 | }; 68 | 69 | /** 70 | Gets the number or votes. 71 | 72 | @public 73 | @param name {string} the name of the place 74 | @param resultHandler {function} the result handler. The votes are given in a JSON object using the "up" and "down" fields. 75 | */ 76 | this.get = function(name, resultHandler) { 77 | var __args = arguments; 78 | if (__args.length === 2 && typeof __args[0] === 'string' && typeof __args[1] === 'function') { 79 | if (closed) { 80 | throw new Error('Proxy is closed'); 81 | } 82 | j_eb.send(j_address, {"name":__args[0]}, {"action":"get"}, function(err, result) { __args[1](err, result &&result.body); }); 83 | return; 84 | } else throw new TypeError('function invoked with invalid arguments'); 85 | }; 86 | 87 | }; 88 | 89 | /** 90 | The method used to create proxy to consume the service. 91 | 92 | @memberof module:vertx-microservice-workshop-js/recommendation_service 93 | @param vertx {Vertx} vert.x 94 | @param address {string} the address where the service is served 95 | @return {RecommendationService} the proxy 96 | */ 97 | RecommendationService.createProxy = function(vertx, address) { 98 | var __args = arguments; 99 | if (__args.length === 2 && typeof __args[0] === 'object' && __args[0]._jdel && typeof __args[1] === 'string') { 100 | if (closed) { 101 | throw new Error('Proxy is closed'); 102 | } 103 | j_eb.send(j_address, {"vertx":__args[0], "address":__args[1]}, {"action":"createProxy"}); 104 | return; 105 | } else throw new TypeError('function invoked with invalid arguments'); 106 | }; 107 | 108 | if (typeof exports !== 'undefined') { 109 | if (typeof module !== 'undefined' && module.exports) { 110 | exports = module.exports = RecommendationService; 111 | } else { 112 | exports.RecommendationService = RecommendationService; 113 | } 114 | } else { 115 | return RecommendationService; 116 | } 117 | }); -------------------------------------------------------------------------------- /recommendation-service/src/main/resources/vertx-microservice-workshop-js/recommendation_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-microservice-workshop-js/recommendation_service */ 18 | var utils = require('vertx-js/util/utils'); 19 | var Vertx = require('vertx-js/vertx'); 20 | 21 | var io = Packages.io; 22 | var JsonObject = io.vertx.core.json.JsonObject; 23 | var JRecommendationService = io.vertx.workshop.recommendation.RecommendationService; 24 | 25 | /** 26 | A service to recommend places. 27 | 28 | @class 29 | */ 30 | var RecommendationService = function(j_val) { 31 | 32 | var j_recommendationService = j_val; 33 | var that = this; 34 | 35 | /** 36 | Votes for a place. 37 | When a vote is placed, the new rating of the place are brodcasted to the event bus. 38 | 39 | @public 40 | @param name {string} the name of the place 41 | @param plus {boolean} whether or not you liked the place. 42 | @param resultHandler {function} invoked when the action is completed 43 | */ 44 | this.vote = function(name, plus, resultHandler) { 45 | var __args = arguments; 46 | if (__args.length === 3 && typeof __args[0] === 'string' && typeof __args[1] ==='boolean' && typeof __args[2] === 'function') { 47 | j_recommendationService["vote(java.lang.String,boolean,io.vertx.core.Handler)"](name, plus, function(ar) { 48 | if (ar.succeeded()) { 49 | resultHandler(null, null); 50 | } else { 51 | resultHandler(null, ar.cause()); 52 | } 53 | }); 54 | } else throw new TypeError('function invoked with invalid arguments'); 55 | }; 56 | 57 | /** 58 | Gets the number or votes. 59 | 60 | @public 61 | @param name {string} the name of the place 62 | @param resultHandler {function} the result handler. The votes are given in a JSON object using the "up" and "down" fields. 63 | */ 64 | this.get = function(name, resultHandler) { 65 | var __args = arguments; 66 | if (__args.length === 2 && typeof __args[0] === 'string' && typeof __args[1] === 'function') { 67 | j_recommendationService["get(java.lang.String,io.vertx.core.Handler)"](name, function(ar) { 68 | if (ar.succeeded()) { 69 | resultHandler(utils.convReturnJson(ar.result()), null); 70 | } else { 71 | resultHandler(null, ar.cause()); 72 | } 73 | }); 74 | } else throw new TypeError('function invoked with invalid arguments'); 75 | }; 76 | 77 | // A reference to the underlying Java delegate 78 | // NOTE! This is an internal API and must not be used in user code. 79 | // If you rely on this property your code is likely to break if we change it / remove it without warning. 80 | this._jdel = j_recommendationService; 81 | }; 82 | 83 | /** 84 | The method used to create proxy to consume the service. 85 | 86 | @memberof module:vertx-microservice-workshop-js/recommendation_service 87 | @param vertx {Vertx} vert.x 88 | @param address {string} the address where the service is served 89 | @return {RecommendationService} the proxy 90 | */ 91 | RecommendationService.createProxy = function(vertx, address) { 92 | var __args = arguments; 93 | if (__args.length === 2 && typeof __args[0] === 'object' && __args[0]._jdel && typeof __args[1] === 'string') { 94 | return utils.convReturnVertxGen(JRecommendationService["createProxy(io.vertx.core.Vertx,java.lang.String)"](vertx._jdel, address), RecommendationService); 95 | } else throw new TypeError('function invoked with invalid arguments'); 96 | }; 97 | 98 | // We export the Constructor function 99 | module.exports = RecommendationService; -------------------------------------------------------------------------------- /scripts/launch-grafana.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run -i -p 3000:3000 grafana/grafana 4 | -------------------------------------------------------------------------------- /scripts/launch-hawkular.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker run -t -i -p 8090:8080 jboss/hawkular-aio 3 | -------------------------------------------------------------------------------- /scripts/launch-mongo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker run --name some-mongo -d -p 27017:27017 mongo -------------------------------------------------------------------------------- /scripts/launch-redis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker run --name some-redis -d -p 6379:6379 redis 3 | -------------------------------------------------------------------------------- /scripts/provision-docker-registry.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export host=localhost 4 | mkdir ../../docker-registry-data 5 | docker run -d -p 5000:5000 --restart=always --name registry \ 6 | -v `pwd`../../docker-registry-data:/var/lib/registry \ 7 | registry:2 8 | docker pull mongo && docker tag mongo ${host}:5000/mongo 9 | docker pull redis && docker tag redis ${host}:5000/redis 10 | docker pull hawkular/hawkular && docker tag hawkular/hawkular ${host}:5000/hawkular 11 | docker pull grafana/grafana && docker tag grafana/grafana ${host}:5000/grafana 12 | docker pull jboss/hawkular-aio && docker tag jboss/hawkular-aio ${host}:5000/hawkular-aio 13 | docker pull graylog2/allinone && docker tag graylog2/allinone ${host}:5000/allinone 14 | docker pull java:openjdk-8u66-jre && docker tag java:openjdk-8u66-jre ${host}:5000/java 15 | -------------------------------------------------------------------------------- /scripts/stop-mongo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | docker stop some-mongo 3 | docker rm some-mongo -------------------------------------------------------------------------------- /scripts/stop-redis.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | docker stop some-redis 3 | docker rm some-redis --------------------------------------------------------------------------------