I currently have a canvas on my Vaadin page to draw bounding boxes, in which they will be saved as objects into an array containing their width, height, coordinates etc. I also have a Vertx backend that can carry out GET and POST requests from my MariaDB database (using JDBC) that is supposed to contain all the bounding boxes objects from the Vaadin frontend (but they only contain sample created objects for now). I would like to know how do I write a POST request properly on the Vaadin frontend using Unirest, so that I can pass my object from Vaadin to Vertx using a 'Save' button, as my current code in UserService.java right now isn't doing anything.
Vaadin UserService.java:
package com.vaadin.starter.beveragebuddy.service;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.vaadin.starter.beveragebuddy.ui.components.BoundingBox;
import com.vaadin.starter.beveragebuddy.ui.components.Canvas;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import static com.helger.commons.mock.CommonsAssert.assertEquals;
//import static com.sun.xml.internal.ws.spi.db.BindingContextFactory.LOGGER;
public class UserService {
public JSONArray bbJsonArray;
public JSONObject bbJsonObject;
public static ArrayList<BoundingBox> bb = Canvas.getArrayBoxes();
public static String createAnnotations(String boxname) throws UnirestException {
Map<String, String> headers = new HashMap<>();
try {
HttpResponse<JsonNode> jsonResponse = Unirest.post("http://localhost:9080/api/annotations")
.header("accept", "application/json")
.field("boxname", bb.get(0).boxname)
.asJson();
JSONObject output = jsonResponse.getBody().getObject();
String status = output.getString("status");
System.out.println(jsonResponse.getBody());
return status;
} catch (UnirestException e) {
return "error";
}
}
}
Vaadin BoundingBox.java:
package com.vaadin.starter.beveragebuddy.ui.components;
import org.json.JSONObject;
public class BoundingBox {
public double xcoordi = 0;
public double ycoordi = 0;
public double boxWidth = 0;
public double boxHeight = 0;
public double endX = 0;
public double endY = 0;
public String picID;
public String boxname;
public String boxcategory;
public String boxcolour;
public BoundingBox(String picID, String boxname, String boxcategory, String boxcolour, double xcoordi, double ycoordi, double boxWidth, double boxHeight, double endX, double endY) {
this.picID = picID;
this.boxname = boxname;
this.boxcategory = boxcategory;
this.boxcolour = boxcolour;
this.xcoordi = xcoordi;
this.ycoordi = ycoordi;
this.boxWidth = boxWidth;
this.boxHeight = boxHeight;
this.endX = endX;
this.endY = endY;
}
public String getPicID() {
return picID;
}
public void setPicID(String picID) {
this.picID = picID;
}
public String getBoxName() {
return boxname;
}
public void setName(String boxname) {
this.boxname = boxname;
}
public String getBoxcategory() {
return boxcategory;
}
public void setBoxcategory(String boxcategory) {
this.boxcategory = boxcategory;
}
public String getBoxcolour() {
return boxcolour;
}
public void setBoxcolour(String boxcolour) {
this.boxcolour = boxcolour;
}
public double getXcoordi() {
return xcoordi;
}
public void setXcoordi(double xcoordi) {
this.xcoordi = xcoordi;
}
public double getYcoordi() {
return ycoordi;
}
public void setYcoordi(double ycoordi) {
this.ycoordi = ycoordi;
}
public double getWidth() {
return boxWidth;
}
public void setWidth(double endX, double xcoordi) {
boxWidth = endX - xcoordi;
}
public double getHeight() {
return boxHeight;
}
public void setHeight(double endY, double ycoordi) {
boxHeight = endY - ycoordi;
}
public double getEndX() { // Bottom-right X coordinate of box
return endX;
}
public void setEndX(double endX) {
this.endX = endX;
}
public double getEndY() { // // Bottom-right Y coordinate of box
return endY;
}
public void setEndY(double endY) {
this.endY = endY;
}
public JSONObject toJSON() {
JSONObject bbJsonObject = new JSONObject();
bbJsonObject.put("boxname", boxname);
bbJsonObject.put("boxcategory", boxcategory);
bbJsonObject.put("boxcolour", boxcolour);
bbJsonObject.put("xcoordi", xcoordi);
bbJsonObject.put("ycoordi", ycoordi);
bbJsonObject.put("boxwidth", boxWidth);
bbJsonObject.put("boxheight", boxHeight);
return bbJsonObject;
}
@Override
public String toString() {
return "{" +
"Name=" + boxname +
", Class=" + boxcategory +
", Colour=" + boxcolour +
", X=" + xcoordi +
", Y=" + ycoordi +
", Width=" + boxWidth +
", Height=" + boxHeight +
'}';
}
}
Vertx MainVerticle.java:
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.jdbc.JDBCClient;
import io.vertx.ext.sql.ResultSet;
import io.vertx.ext.sql.SQLClient;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.ext.jdbc.JDBCClient;
import io.vertx.ext.sql.SQLConnection;
import io.vertx.ext.sql.UpdateResult;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.starter.components.BoundingBox;
import io.vertx.starter.components.Picture;
import java.sql.Statement;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MainVerticle extends AbstractVerticle {
private JDBCClient jdbc;
private static final Logger LOGGER = LoggerFactory.getLogger(MainVerticle.class);
private Future fut;
private String SQL_GET_ANNOTATION = "SELECT * FROM boundingboxes";
private String SQL_ADD_ANNOTATION = "INSERT INTO boundingboxes (picID, boxname, boxcategory, boxcolour, xcoordi, ycoordi, boxWidth, boxHeight, endX, endY) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
private int nextCount = 0;
private Map<Integer, Picture> pictures = new LinkedHashMap<>();
private Map<Integer, BoundingBox> annotations = new LinkedHashMap<>();
@Override
public void start(Future<Void> fut) {
createBBData();
startHttpServer();
dbConnection();
// Router router = Router.router(vertx);
}
public Future<Void> dbConnection(){
// Create a JDBC client
jdbc = JDBCClient.createShared(vertx, new JsonObject()
.put("url", "jdbc:mariadb://localhost:3306/annotationdb")
.put("driver_name", "org.mariadb.jdbc.Driver")
.put("max_pool_size", 5)
.put("user", "root"));
jdbc.getConnection(ar -> {
if (ar.failed()) {
LOGGER.error("Could not open a database connection", ar.cause());
fut.fail(ar.cause()); // If SQL Connection could not be obtained, then method future is completed to fail.
} else {
SQLConnection connection = ar.result(); // else ... SQL connection is established with successful AsyncResult (Use this connection to perform a SQL query)
connection.query(SQL_GET_ANNOTATION, get -> {
connection.close();
if (get.failed()) { // If SQL Query fails ...
LOGGER.error("Database operation error", get.cause()); // ...
fut.fail(get.cause());
} else {
LOGGER.info("Connection Established");
// fut.complete(); // Complete method future object with a success
System.out.print("Succeed");
ResultSet rs = get.result();
if (rs.getNumRows() > 0) {
System.out.println(rs.getResults().toString());
nextCount++;
}
}
});
}
});
return fut;
}
public Future<Void> startHttpServer(){
Future<Void> future = Future.future();
HttpServer server = vertx.createHttpServer();
Router router = Router.router(vertx);
router.route("/").handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response
.putHeader("content-type", "text/html")
.end("<h1>Hello from my first Vert.x 3 application</h1>");
});
// Serve static resources from the /assets directory
router.route("/assets/*").handler(StaticHandler.create("assets"));
router.route("/api/annotations*").handler(BodyHandler.create());
router.get("/api/annotations").handler(this::getAll);
// router.get("/api/annotations/:bbID").handler(this::getOne);
router.post("/api/annotations").handler(this::addOne);
router.put("/api/annotations/:bbID").handler(this::updateOne);
router.delete("/api/annotations/:bbID").handler(this::deleteOne);
vertx
.createHttpServer()
.requestHandler(router::accept)
.listen(
// Retrieve the port from the configuration,
// default to 8080.
config().getInteger("http.port", 9080)
);
return future;
}
private void createBBData() {
BoundingBox annotation1 = new BoundingBox("P1", "sign1", "signs", "Magenta", 340.0, 110.0, 100.0, 200.0, 500.0, 400.0);
annotations.put(annotation1.getBbID(), annotation1);
BoundingBox annotation2 = new BoundingBox("P2", "woman1", "people", "Red", 300.0, 150.0, 200.0, 400.0, 600.0, 350.0);
annotations.put(annotation2.getBbID(), annotation2);
}
private void getAll(RoutingContext routingContext) {
jdbc.getConnection(ar -> {
SQLConnection connection = ar.result();
connection.query("SELECT * FROM boundingboxes", result -> {
System.out.println("[GetALL] NUMBER = " + result.result().getNumRows());
LOGGER.info("LOGGER INFO TEST!!");
LOGGER.error("LOGGER ERROR TEST!!");
// System.out.println("[GetALL] CONTENTS = " + result.result().getResults().get(0).encodePrettily());
List<BoundingBox> annotation = result.result().getRows().stream().map(BoundingBox::new).collect(Collectors.toList());
routingContext.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(annotation));
connection.close();
});
});
}
private void addOne(RoutingContext routingContext) { // OK
jdbc.getConnection(ar -> {
System.out.println("CONNECTION INSERT OK");
final BoundingBox annotation = Json.decodeValue(routingContext.getBodyAsString(),
BoundingBox.class);
System.out.println(annotation.getEndX());
SQLConnection connection = ar.result();
// annotations.put(annotation.getBbID(), annotation);
insert(annotation, connection, (r) ->
routingContext.response()
.setStatusCode(201)
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(r.result())));
connection.close();
});
}
private void updateOne(RoutingContext routingContext) {
final String bbID = routingContext.request().getParam("bbID");
JsonObject json = routingContext.getBodyAsJson();
if (bbID == null || json == null) {
routingContext.response().setStatusCode(400).end();
} else {
jdbc.getConnection(ar ->
update(bbID, json, ar.result(), (annotation) -> {
if (annotation.failed()) {
routingContext.response().setStatusCode(404).end();
} else {
routingContext.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(annotation.result()));
}
ar.result().close();
})
);
}
}
private void select(String bbID, SQLConnection connection, Handler<AsyncResult<BoundingBox>> resultHandler) {
System.out.println("[TEST] SELECT ONE REQUEST !!!");
connection.queryWithParams("SELECT * FROM boundingboxes", new JsonArray().add(bbID), ar -> {
if (ar.failed()) {
resultHandler.handle(Future.failedFuture("Annotation not found"));
} else {
if (ar.result().getNumRows() >= 1) {
System.out.println(ar.result().getResults().get(0).encodePrettily());
System.out.println(ar.result().getRows().get(0).fieldNames());
System.out.println(ar.result().getRows().get(0).encodePrettily());
resultHandler.handle(Future.succeededFuture(new BoundingBox(ar.result().getRows().get(0))));
} else {
System.out.println("Annotation is not found!");
resultHandler.handle(Future.failedFuture("Annotation not found"));
}
}
});
}
private void insert(BoundingBox annotation, SQLConnection connection, Handler<AsyncResult<BoundingBox>> next) { // OK
String sql = "INSERT INTO boundingboxes (picID, boxname, boxcategory, boxcolour, xcoordi, ycoordi, boxWidth, boxHeight, endX, endY) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
JsonArray annotationJson = new JsonArray().add(annotation.getPicID()).add(annotation.getBoxName()).add(annotation.getBoxcategory()).add(annotation.getBoxcolour()).add(annotation.getXcoordi()).add(annotation.getYcoordi()).add(annotation.getWidth()).add(annotation.getHeight()).add(annotation.getEndX()).add(annotation.getEndX());
// System.out.println(annotationJson);
connection.updateWithParams(sql, annotationJson,
(ar) -> {
if (ar.failed()) {
next.handle(Future.failedFuture(ar.cause()));
connection.close();
return;
}
UpdateResult result = ar.result();
BoundingBox bb = new BoundingBox(result.getKeys().getInteger(0), annotation.getPicID(), annotation.getBoxName(), annotation.getBoxcategory(), annotation.getBoxcolour(), annotation.getXcoordi(), annotation.getYcoordi(), annotation.getWidth(), annotation.getHeight(), annotation.getEndX(), annotation.getEndY());
System.out.println(bb);
next.handle(Future.succeededFuture(bb));
});
}
private void update(String bbID, JsonObject content, SQLConnection connection,
Handler<AsyncResult<BoundingBox>> resultHandler) {
String sql = "UPDATE boundingboxes SET picID=?, boxname=?, boxcategory=?, boxcolour=?, xcoordi=?, ycoordi=?, boxWidth=?, boxHeight=?, endX=?, endY=? WHERE bbID=?";
connection.updateWithParams(sql,
new JsonArray().add(content.getString("picID")).add(content.getString("boxname")).add(content.getString("boxcategory")).add(content.getString("boxcolour")).add(content.getDouble("xcoordi")).add(content.getDouble("ycoordi")).add(content.getDouble("boxWidth")).add(content.getDouble("boxHeight")).add(content.getDouble("endX")).add(content.getDouble("endY")).add(bbID),
update -> {
if (update.failed()) {
resultHandler.handle(Future.failedFuture("Cannot update the annotation"));
return;
}
if (update.result().getUpdated() == 0) {
resultHandler.handle(Future.failedFuture("Annotation not found"));
return;
}
resultHandler.handle(
Future.succeededFuture(new BoundingBox(Integer.valueOf(bbID),
content.getString("picID"), content.getString("boxname"), content.getString("boxcategory"), content.getString("boxcolour"), content.getDouble("xcoordi"), content.getDouble("ycoordi"), content.getDouble("boxWidth"), content.getDouble("boxHeight"), content.getDouble("endX"), content.getDouble("endY"))));
});
}
}
Vertx index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>List of Annotations</title>
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container" role="main">
<div class="jumbotron">
<h1>List of Annotations</h1>
<p>Just an example of simple CRUD application developed using Vert.x and Vertx Web.</p>
</div>
<div class="page-header">
<h1>Annotations</h1>
<button class="pull-right btn btn-primary annotation-add" data-action="add" data-toggle="modal"
data-target="#annotationModal">
<span class="glyphicon glyphicon-plus"></span> Add a new annotation
</button>
<div class="row">
<div class="col-md-12">
<table class="table table-striped">
<thead>
<tr>
<th>bbID</th>
<th>picID</th>
<th>Box Name</th>
<th>Box Category</th>
<th>Box Colour</th>
<th>X-Coordi</th>
<th>Y-Coordi</th>
<th>Box Width</th>
<th>Box Height</th>
<th>End X</th>
<th>End Y</th>
<th></th>
</tr>
</thead>
<tbody id="content">
<!-- filled using Ajax -->
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
$(function () {
load();
initModal();
});
function create(picID, boxname, boxcategory, boxcolour, xcoordi, ycoordi, boxWidth, boxHeight, endX, endY) {
$.post("/api/annotations", JSON.stringify({picID: picID, boxname: boxname, boxcategory: boxcategory, boxcolour: boxcolour, xcoordi: xcoordi, ycoordi: ycoordi, boxWidth: boxWidth, boxHeight: boxHeight, endX: endX, endY: endY}), function () {
load();
}, "json");
}
function remove(bbID) {
$.ajax({
method: "DELETE",
url: "/api/annotations/" + bbID
}).done(function () {
load();
});
}
function update(bbID, picID, boxname, boxcategory, boxcolour, xcoordi, ycoordi, boxWidth, boxHeight, endX, endY) {
$.ajax({
method: "PUT",
url: "/api/annotations/" + bbID,
data: JSON.stringify({picID: picID, boxname: boxname, boxcategory: boxcategory, boxcolour: boxcolour, xcoordi: xcoordi, ycoordi: ycoordi, boxWidth: boxWidth, boxHeight: boxHeight, endX: endX, endY: endY})
}).done(function () {
load();
});
}
function load() {
$("#content").children().remove();
$.getJSON("/api/annotations", function (data) {
$.each(data, function (key, val) {
$("<tr><td>" + val.bbID + "</td><td>" + val.picID + "</td><td>" + val.boxname + "</td><td>" + val.boxcategory + "</td><td>" + val.boxcolour + "</td><td>" + val.xcoordi + "</td><td>" + val.ycoordi + "</td><td>" + val.boxWidth + "</td><td>" + val.boxHeight + "</td><td>" + val.endX + "</td><td>" + val.endY + "</td>" +
"<td>" +
"<button data-action='edit' class='btn btn-primary btn-sm annotation-edit' " +
"data-toggle='modal' " +
"data-target='#annotationModal' " +
"data-bbID='" + val.bbID + "' " +
"data-boxname='" + val.boxname + "' " +
"data-boxcategory='" + val.boxcategory + "' " +
"data-boxcolour='" + val.boxcolour + "'>" +
<!--"data-xcoordi='" + val.xcoordi + "'>" +-->
<!--"data-ycoordi='" + val.ycoordi + "'>" +-->
<!--"data-boxWidth='" + val.boxWidth + "'>" +-->
<!--"data-boxHeight='" + val.boxHeight + "'>" +-->
<!--"data-endX='" + val.endX + "'>" +-->
<!--"data-endY='" + val.endY + "'>" +-->
"<span class='glyphicon glyphicon-pencil'></span>" +
"</button>" +
" " +
"<button class='btn btn-danger btn-sm annotation-delete' data-bbID='" + val.bbID + "'>" +
" <span class='glyphicon glyphicon-minus'></span>" +
"</button>" +
"</td>" +
"</tr>").appendTo("#content");
});
initCallbacks();
});
}
function initCallbacks() {
$(".annotation-delete").unbind().click(function() {
var bbID = $(this).data("bbID");
remove(bbID);
});
}
function initModal() {
$("#annotationModal").on('show.bs.modal', function (event) {
var button = $(event.relatedTarget);
var action = button.data('action');
var bbID = button.data('bbID');
var annotationAction = $("#annotationAction");
annotationAction.unbind();
var modal = $(this);
if (action === "add") {
modal.find('.modal-title').text("Add an annotation");
modal.find('#annotation-picID').val("");
modal.find('#annotation-boxname').val("");
modal.find('#annotation-boxcategory').val("");
modal.find('#annotation-boxcolour').val("");
modal.find('#annotation-xcoordi').val("");
modal.find('#annotation-ycoordi').val("");
modal.find('#annotation-boxWidth').val("");
modal.find('#annotation-boxHeight').val("");
modal.find('#annotation-endX').val("");
modal.find('#annotation-endY').val("");
annotationAction.click(function () {
create($("#annotation-picID").val(), $("#annotation-boxname").val(), $("#annotation-boxcategory").val(), $("#annotation-boxcolour").val(), $("#annotation-xcoordi").val(), $("#annotation-ycoordi").val(), $("#annotation-boxWidth").val(), $("#annotation-boxHeight").val(), $("#annotation-endX").val(), $("#annotation-endY").val());
$('#annotationModal').modal('toggle');
});
} else {
modal.find('.modal-title').text("Edit an annotation");
modal.find('#annotation-picID').val(button.data("picID"));
modal.find('#annotation-boxname').val(button.data("boxname"));
modal.find('#annotation-boxcategory').val(button.data("boxcategory"));
modal.find('#annotation-boxcolour').val(button.data("boxcolour"));
modal.find('#annotation-xcoordi').val(button.data("xcoordi"));
modal.find('#annotation-ycoordi').val(button.data("ycoordi"));
modal.find('#annotation-boxWidth').val(button.data("boxWidth"));
modal.find('#annotation-boxHeight').val(button.data("boxHeight"));
modal.find('#annotation-endX').val(button.data("endX"));
modal.find('#annotation-endY').val(button.data("endY"));
annotationAction.click(function () {
update(id, $("#annotation-picID").val(), $("#annotation-boxname").val(), $("#annotation-boxcategory").val(), $("#annotation-boxcolour").val(), $("#annotation-xcoordi").val(), $("#annotation-ycoordi").val(), $("#annotation-boxWidth").val(), $("#annotation-boxHeight").val(), $("#annotation-endX").val(), $("#annotation-endY").val());
$('#annotationModal').modal('toggle');
});
}
})
}
</script>
<div class="modal fade" id="annotationModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
<h4 class="modal-title" id="annotationModalTitle">Add an annotation</h4>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="annotation-picID" class="control-label">Pic ID:</label>
<input type="text" class="form-control" id="annotation-picID">
</div>
<div class="form-group">
<label for="annotation-boxname" class="control-label">Box Name:</label>
<input type="text" class="form-control" id="annotation-boxname">
</div>
<div class="form-group">
<label for="annotation-boxcategory" class="control-label">Box Category:</label>
<input type="text" class="form-control" id="annotation-boxcategory">
</div>
<div class="form-group">
<label for="annotation-boxcolour" class="control-label">Box Colour:</label>
<input type="text" class="form-control" id="annotation-boxcolour">
</div>
<div class="form-group">
<label for="annotation-xcoordi" class="control-label">X-Coordi:</label>
<input type="text" class="form-control" id="annotation-xcoordi">
</div>
<div class="form-group">
<label for="annotation-ycoordi" class="control-label">Y-Coordi:</label>
<input type="text" class="form-control" id="annotation-ycoordi">
</div>
<div class="form-group">
<label for="annotation-boxWidth" class="control-label">Box Width:</label>
<input type="text" class="form-control" id="annotation-boxWidth">
</div>
<div class="form-group">
<label for="annotation-boxHeight" class="control-label">Box Height:</label>
<input type="text" class="form-control" id="annotation-boxHeight">
</div>
<div class="form-group">
<label for="annotation-endX" class="control-label">End X:</label>
<input type="text" class="form-control" id="annotation-endX">
</div>
<div class="form-group">
<label for="annotation-endY" class="control-label">End Y:</label>
<input type="text" class="form-control" id="annotation-endY">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" id="annotationAction" class="btn btn-primary">Save</button>
</div>
</div>
</div>
</div>
</body>
</html>
Any help is appreciated, thank you!