/*
* Copyright 2014 Red Hat, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.example.web.jdbc;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.example.util.Runner;
import io.vertx.ext.jdbc.JDBCClient;
import io.vertx.ext.sql.SQLConnection;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
/**
* @author <a href="mailto:pmlopes@gmail.com">Paulo Lopes</a>
*/
public class Server extends AbstractVerticle {
// Convenience method so you can run it in your IDE
public static void main(String[] args) {
Runner.runExample(Server.class);
}
private JDBCClient client;
@Override
public void start() {
Server that = this;
// Create a JDBC client with a test database
client = JDBCClient.createShared(vertx, new JsonObject()
.put("url", "jdbc:hsqldb:mem:test?shutdown=true")
.put("driver_class", "org.hsqldb.jdbcDriver"));
setUpInitialData(ready -> {
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
// in order to minimize the nesting of call backs we can put the JDBC connection on the context for all routes
// that match /products
// this should really be encapsulated in a reusable JDBC handler that uses can just add to their app
router.route("/products*").handler(routingContext -> client.getConnection(res -> {
if (res.failed()) {
routingContext.fail(res.cause());
} else {
SQLConnection conn = res.result();
// save the connection on the context
routingContext.put("conn", conn);
// we need to return the connection back to the jdbc pool. In order to do that we need to close it, to keep
// the remaining code readable one can add a headers end handler to close the connection. The reason to
// choose the headers end is that if the close of the connection or say for example end of transaction
// results in an error, it is still possible to return back to the client an error code and message.
routingContext.addHeadersEndHandler(done -> conn.close(close -> {
if (close.failed()) {
done.fail(close.cause());
} else {
done.complete();
}
}));
routingContext.next();
}
})).failureHandler(routingContext -> {
SQLConnection conn = routingContext.get("conn");
if (conn != null) {
conn.close(v -> {
});
}
});
router.get("/products/:productID").handler(that::handleGetProduct);
router.post("/products").handler(that::handleAddProduct);
router.get("/products").handler(that::handleListProducts);
vertx.createHttpServer().requestHandler(router::accept).listen(8080);
});
}
private void handleGetProduct(RoutingContext routingContext) {
String productID = routingContext.request().getParam("productID");
HttpServerResponse response = routingContext.response();
if (productID == null) {
sendError(400, response);
} else {
SQLConnection conn = routingContext.get("conn");
conn.queryWithParams("SELECT id, name, price, weight FROM products where id = ?", new JsonArray().add(Integer.parseInt(productID)), query -> {
if (query.failed()) {
sendError(500, response);
} else {
if (query.result().getNumRows() == 0) {
sendError(404, response);
} else {
response.putHeader("content-type", "application/json").end(query.result().getRows().get(0).encode());
}
}
});
}
}
private void handleAddProduct(RoutingContext routingContext) {
HttpServerResponse response = routingContext.response();
SQLConnection conn = routingContext.get("conn");
JsonObject product = routingContext.getBodyAsJson();
conn.updateWithParams("INSERT INTO products (name, price, weight) VALUES (?, ?, ?)",
new JsonArray().add(product.getString("name")).add(product.getFloat("price")).add(product.getInteger("weight")), query -> {
if (query.failed()) {
sendError(500, response);
} else {
response.end();
}
});
}
private void handleListProducts(RoutingContext routingContext) {
HttpServerResponse response = routingContext.response();
SQLConnection conn = routingContext.get("conn");
conn.query("SELECT id, name, price, weight FROM products", query -> {
if (query.failed()) {
sendError(500, response);
} else {
JsonArray arr = new JsonArray();
query.result().getRows().forEach(arr::add);
routingContext.response().putHeader("content-type", "application/json").end(arr.encode());
}
});
}
private void sendError(int statusCode, HttpServerResponse response) {
response.setStatusCode(statusCode).end();
}
private void setUpInitialData(Handler<Void> done) {
client.getConnection(res -> {
if (res.failed()) {
throw new RuntimeException(res.cause());
}
final SQLConnection conn = res.result();
conn.execute("CREATE TABLE IF NOT EXISTS products(id INT IDENTITY, name VARCHAR(255), price FLOAT, weight INT)", ddl -> {
if (ddl.failed()) {
throw new RuntimeException(ddl.cause());
}
conn.execute("INSERT INTO products (name, price, weight) VALUES ('Egg Whisk', 3.99, 150), ('Tea Cosy', 5.99, 100), ('Spatula', 1.00, 80)", fixtures -> {
if (fixtures.failed()) {
throw new RuntimeException(fixtures.cause());
}
done.handle(null);
});
});
});
}
}
所有评论(0)