Sfoglia il codice sorgente

Added ExceptionHandler for APIs

Lukas Cerny 1 anno fa
parent
commit
ceeb2dc5f4

+ 1 - 0
src/main/java/cz/senslog/telemetry/app/Application.java

@@ -67,6 +67,7 @@ public final class Application {
                 logger.info("Started in {} second.", uptime() / 1000.0);
             } else {
                 logger.error("Deployment failed! The reason is '{}'", res.cause().getMessage());
+                logger.catching(res.cause());
             }
         });
     }

+ 48 - 0
src/main/java/cz/senslog/telemetry/server/ExceptionHandler.java

@@ -0,0 +1,48 @@
+package cz.senslog.telemetry.server;
+
+import cz.senslog.telemetry.database.DataNotFoundException;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.validation.ParameterProcessorException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Optional;
+
+
+public final class ExceptionHandler implements io.vertx.core.Handler<RoutingContext> {
+
+    private static final Logger logger = LogManager.getLogger(ExceptionHandler.class);
+
+    private static final String DEFAULT_ERROR_MESSAGE = "Something went wrong!";
+
+    public static ExceptionHandler createAsJSON() {
+        return new ExceptionHandler();
+    }
+
+    private static int codeByException(Throwable throwable, int defaultCode) {
+        if (throwable instanceof DataNotFoundException) {
+            return  404;
+        } else if (throwable instanceof ParameterProcessorException) {
+            return  400;
+        }
+        return defaultCode;
+    }
+
+    @Override
+    public void handle(RoutingContext rc) {
+        Throwable th = Optional.ofNullable(rc.failure()).orElse(new Throwable(DEFAULT_ERROR_MESSAGE));
+
+        String message = th.getMessage();
+        int code = codeByException(th, rc.statusCode());
+
+        logger.error(message);
+        rc.response()
+                .putHeader("Content-Type", "application/json")
+                .setStatusCode(rc.statusCode())
+                .end(JsonObject.of(
+                        "code", code,
+                        "message", message
+                ).encode());
+    }
+}

+ 20 - 29
src/main/java/cz/senslog/telemetry/server/HttpVertxServer.java

@@ -5,13 +5,13 @@ import cz.senslog.telemetry.database.repository.MapLogRepository;
 import cz.senslog.telemetry.database.repository.SensLogRepository;
 import cz.senslog.telemetry.utils.ResourcesUtils;
 import io.vertx.core.AbstractVerticle;
-import io.vertx.core.Handler;
 import io.vertx.core.Promise;
 import io.vertx.core.http.HttpMethod;
 import io.vertx.core.json.JsonObject;
 import io.vertx.ext.web.Router;
-import io.vertx.ext.web.RoutingContext;
 import io.vertx.ext.web.handler.CorsHandler;
+import io.vertx.ext.web.handler.LoggerFormat;
+import io.vertx.ext.web.handler.LoggerHandler;
 import io.vertx.ext.web.openapi.RouterBuilder;
 import io.vertx.pgclient.PgPool;
 import io.vertx.sqlclient.RowSet;
@@ -19,8 +19,6 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import java.nio.file.Path;
-import java.util.function.Consumer;
-import java.util.function.Function;
 
 import static java.util.Objects.requireNonNull;
 
@@ -35,12 +33,24 @@ public final class HttpVertxServer extends AbstractVerticle {
                 .onSuccess(openAPIRouterBuilder -> {
                     logger.info("The OpenAPI specification was loaded successfully.");
 
+                    openAPIRouterBuilder.rootHandler(LoggerHandler.create(false, LoggerFormat.SHORT));
+
+                    openAPIRouterBuilder.rootHandler(CorsHandler.create()
+                            .allowedMethod(HttpMethod.GET)
+
+                            .allowedHeader("x-requested-with")
+                            .allowedHeader("Access-Control-Allow-Origin")
+                            .allowedHeader("Origin")
+                            .allowedHeader("Content-Type")
+                            .allowedHeader("Accept")
+                    );
+
                     PgPool pgPool = ConnectionPool.createWithVertx(vertx, config());
 
                     pgPool.query("SELECT version()").execute().map(RowSet::iterator)
                             .map(it -> it.hasNext() ? it.next().getString(0) : null)
                             .onSuccess(version -> logger.info("Successful database connection to {}.", version))
-                            .onFailure(logger::catching);
+                            .onFailure(startPromise::fail);
 
                     SensLogRepository repo = MapLogRepository.create(pgPool);
                     OpenAPIHandler apiHandler = OpenAPIHandler.create(repo);
@@ -88,32 +98,13 @@ public final class HttpVertxServer extends AbstractVerticle {
                     openAPIRouterBuilder.operation("eventIdObservationsGET").handler(apiHandler::eventIdObservationsGET);
                     openAPIRouterBuilder.operation("eventIdLocationsGET").handler(apiHandler::eventIdLocationsGET);
 
-
                     Router mainRouter = openAPIRouterBuilder.createRouter();
-//                    mainRouter.route().handler(LoggerHandler.create());
-//                    mainRouter.route().handler(rc -> logger.info("HTTP Request '{}'.", rc.request().absoluteURI()));
-
-
-                    mainRouter.route().failureHandler(cr -> {
-                        logger.error(cr.failure().getMessage());
-                        cr.response()
-                                .putHeader("Content-Type", "application/json")
-                                .setStatusCode(cr.statusCode())
-                                .end(JsonObject.of(
-                                        "code", cr.statusCode(),
-                                        "message", cr.failure().getMessage()
-                                ).encode());
-                    });
-
-                    mainRouter.route().handler(CorsHandler.create()
-                            .allowedMethod(HttpMethod.GET)
 
-                            .allowedHeader("x-requested-with")
-                            .allowedHeader("Access-Control-Allow-Origin")
-                            .allowedHeader("Origin")
-                            .allowedHeader("Content-Type")
-                            .allowedHeader("Accept")
-                    );
+                    mainRouter.route().failureHandler(ExceptionHandler.createAsJSON());
+
+                    mainRouter.errorHandler(404, h -> h.fail(404,
+                            new Throwable(String.format("Resource '%s' not found.", h.request().uri()))
+                    ));
 
                     JsonObject serverConfig = config().getJsonObject("server");
                     vertx.createHttpServer()

+ 35 - 48
src/main/java/cz/senslog/telemetry/server/OpenAPIHandler.java

@@ -1,7 +1,6 @@
 package cz.senslog.telemetry.server;
 
 import cz.senslog.telemetry.app.Application;
-import cz.senslog.telemetry.database.DataNotFoundException;
 import cz.senslog.telemetry.database.SortType;
 import cz.senslog.telemetry.database.repository.SensLogRepository;
 import cz.senslog.telemetry.utils.TernaryCondition;
@@ -9,8 +8,6 @@ import io.vertx.core.http.HttpServerRequest;
 import io.vertx.core.json.JsonArray;
 import io.vertx.core.json.JsonObject;
 import io.vertx.ext.web.RoutingContext;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
 
 
 import java.time.OffsetDateTime;
@@ -24,7 +21,6 @@ import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
 import static java.util.stream.Collectors.toList;
 
 public class OpenAPIHandler {
-    private static final Logger logger = LogManager.getLogger(OpenAPIHandler.class);
 
     private static final int DEFAULT_MAX_DATA_LIMIT = 500;
     private static final ZoneId DEFAULT_ZONE_ID = ZoneId.of("UTC");
@@ -48,15 +44,6 @@ public class OpenAPIHandler {
     }
 
 
-    private static final BiConsumer<Throwable, Consumer<Integer>> throwableToStatusCode = (th, handler) -> {
-        if (th instanceof DataNotFoundException) {
-            handler.accept(404);
-        } else {
-            handler.accept(400);
-        }
-    };
-
-
     public void info(RoutingContext rc) {
         rc.response().end(JsonObject.of(
                     "name", Application.PROJECT_NAME,
@@ -86,7 +73,7 @@ public class OpenAPIHandler {
                                 "fromTime", DATE_TIME_FORMATTER.apply(c.getFromTime(), zone),
                                 "toTime", DATE_TIME_FORMATTER.apply(c.getToTime(), zone)
                                 ))).collect(toList())).encode()))
-                .onFailure(th -> rc.fail(400, th));
+                .onFailure(rc::fail);
     }
 
     public void campaignIdGET(RoutingContext rc) {
@@ -112,7 +99,7 @@ public class OpenAPIHandler {
                                 "fromTime", DATE_TIME_FORMATTER.apply(c.getFromTime(), zone),
                                 "toTime", DATE_TIME_FORMATTER.apply(c.getToTime(), zone)
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void campaignIdUnitsGET(RoutingContext rc) {
@@ -138,7 +125,7 @@ public class OpenAPIHandler {
                                 "fromTime", DATE_TIME_FORMATTER.apply(u.getFromTime(), zone),
                                 "toTime", DATE_TIME_FORMATTER.apply(u.getToTime(), zone)
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void campaignIdUnitsObservationsGET(RoutingContext rc) {
@@ -215,7 +202,7 @@ public class OpenAPIHandler {
                                                 "altitude", o.getLocation().getAltitude()),
                                         "observedValues", o.getObservedValues()
                                 )).collect(toList())))).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void campaignIdUnitIdObservationsGET(RoutingContext rc) {
@@ -292,7 +279,7 @@ public class OpenAPIHandler {
                                                         "altitude", o.getLocation().getAltitude()),
                                                 "observedValues", o.getObservedValues()
                                         )).collect(toList())))).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void campaignIdUnitsObservationsLocationsGET(RoutingContext rc) {
@@ -353,7 +340,7 @@ public class OpenAPIHandler {
                                         l.getLocation().getAltitude()
                                 )
                         )).collect(toList())))).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void campaignIdUnitIdLocationsGET(RoutingContext rc) {
@@ -433,7 +420,7 @@ public class OpenAPIHandler {
                                                     l.getLocation().getAltitude()
                                             )
                                     )).collect(toList())))).encode()))
-                    .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                    .onFailure(rc::fail);
         } else {
             // TODO implement filter
             /*
@@ -462,7 +449,7 @@ public class OpenAPIHandler {
                                 "imei", u.getImei(),
                                 "description", u.getDescription()
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void unitIdSensorsGET(RoutingContext rc) {
@@ -482,7 +469,7 @@ public class OpenAPIHandler {
                                 "name", s.getName(),
                                 "type", s.getType()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void unitsGET(RoutingContext rc) {
@@ -500,7 +487,7 @@ public class OpenAPIHandler {
                                 "name", u.getName(),
                                 "description", u.getDescription()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void unitIdCampaignsGET(RoutingContext rc) {
@@ -524,7 +511,7 @@ public class OpenAPIHandler {
                                 "fromTime", DATE_TIME_FORMATTER.apply(c.getFromTime(), zone),
                                 "toTime", DATE_TIME_FORMATTER.apply(c.getToTime(), zone)
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void sensorIdGET(RoutingContext rc) {
@@ -548,7 +535,7 @@ public class OpenAPIHandler {
                                 "type", s.getType(),
                                 "phenomenon", s.getPhenomenon().getName()
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void sensorsGET(RoutingContext rc) {
@@ -566,7 +553,7 @@ public class OpenAPIHandler {
                                 "name", s.getName(),
                                 "description", s.getDescription()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void sensorIdUnitsGET(RoutingContext rc) {
@@ -586,7 +573,7 @@ public class OpenAPIHandler {
                                 "name", u.getName(),
                                 "description", u.getDescription()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void phenomenonIdGET(RoutingContext rc) {
@@ -607,7 +594,7 @@ public class OpenAPIHandler {
                                 "uom", p.getUom(),
                                 "uomLink", p.getUomLink()
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void phenomenonsGET(RoutingContext rc) {
@@ -624,7 +611,7 @@ public class OpenAPIHandler {
                                 "id", p.getId(),
                                 "name", p.getName()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void phenomenonIdSensorsGET(RoutingContext rc) {
@@ -644,7 +631,7 @@ public class OpenAPIHandler {
                                 "name", s.getName(),
                                 "type", s.getType()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void campaignIdUnitIdGET(RoutingContext rc) {
@@ -675,7 +662,7 @@ public class OpenAPIHandler {
                                 "fromTime", DATE_TIME_FORMATTER.apply(u.getFromTime(), zone),
                                 "toTime", DATE_TIME_FORMATTER.apply(u.getToTime(), zone)
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void campaignIdUnitIdSensorsGET(RoutingContext rc) {
@@ -696,7 +683,7 @@ public class OpenAPIHandler {
                                 "name", s.getName(),
                                 "type", s.getType()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void campaignIdUnitIdSensorIdGET(RoutingContext rc) {
@@ -724,7 +711,7 @@ public class OpenAPIHandler {
                                 "type", s.getType(),
                                 "phenomenon", s.getPhenomenon().getName()
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void campaignIdUnitIdSensorIdObservationsGET(RoutingContext rc) {
@@ -802,7 +789,7 @@ public class OpenAPIHandler {
                                                 "latitude", o.getLocation().getLatitude(),
                                                 "altitude", o.getLocation().getAltitude())
                                 )).collect(toList())))).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void driversGET(RoutingContext rc) {
@@ -819,7 +806,7 @@ public class OpenAPIHandler {
                                 "id", d.getId(),
                                 "name", d.getName()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void driverIdGET(RoutingContext rc) {
@@ -839,7 +826,7 @@ public class OpenAPIHandler {
                                 "id", d.getId(),
                                 "name", d.getName()
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void driverIdUnitsGET(RoutingContext rc) {
@@ -865,7 +852,7 @@ public class OpenAPIHandler {
                                 "name", u.getName(),
                                 "description", u.getDescription()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void driverIdUnitIdGET(RoutingContext rc) {
@@ -888,7 +875,7 @@ public class OpenAPIHandler {
                                 "imei", u.getImei(),
                                 "description", u.getDescription()
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void driverIdUnitIdActionsGET(RoutingContext rc) {
@@ -908,7 +895,7 @@ public class OpenAPIHandler {
                                 "id", a.getId(),
                                 "name", a.getName()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void driverIdActionsGET(RoutingContext rc) {
@@ -927,7 +914,7 @@ public class OpenAPIHandler {
                                 "id", a.getId(),
                                 "name", a.getName()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void driverIdActionIdGET(RoutingContext rc) {
@@ -948,7 +935,7 @@ public class OpenAPIHandler {
                                 "id", a.getId(),
                                 "name", a.getName()
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void driverIdActionIdUnitsGET(RoutingContext rc) {
@@ -969,7 +956,7 @@ public class OpenAPIHandler {
                                 "name", u.getName(),
                                 "description", u.getDescription()
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void driverIdActionIdUnitIdGET(RoutingContext rc) {
@@ -994,7 +981,7 @@ public class OpenAPIHandler {
                                 "imei", u.getImei(),
                                 "description", u.getDescription()
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void driverIdUnitIdActionIdGET(RoutingContext rc) {
@@ -1017,7 +1004,7 @@ public class OpenAPIHandler {
                                 "id", a.getId(),
                                 "name", a.getName()
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void driverIdUnitIdActionIdEventsGET(RoutingContext rc) {
@@ -1042,7 +1029,7 @@ public class OpenAPIHandler {
                                 "fromTime", DATE_TIME_FORMATTER.apply(e.getFromTime(), zone),
                                 "description", DATE_TIME_FORMATTER.apply(e.getToTime(), zone)
                         ))).collect(toList())).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void eventIdGET(RoutingContext rc) {
@@ -1072,7 +1059,7 @@ public class OpenAPIHandler {
                                 "fromTime", DATE_TIME_FORMATTER.apply(e.getFromTime(), zone),
                                 "description", DATE_TIME_FORMATTER.apply(e.getToTime(), zone)
                         )).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void eventIdObservationsGET(RoutingContext rc) {
@@ -1148,7 +1135,7 @@ public class OpenAPIHandler {
                                                 "altitude", o.getLocation().getAltitude()),
                                         "observedValues", o.getObservedValues()
                                 )).collect(toList())))).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 
     public void eventIdLocationsGET(RoutingContext rc) {
@@ -1223,6 +1210,6 @@ public class OpenAPIHandler {
                                                 l.getLocation().getAltitude()
                                         )
                                 )).collect(toList())))).encode()))
-                .onFailure(th -> throwableToStatusCode.accept(th, code -> rc.fail(code, th)));
+                .onFailure(rc::fail);
     }
 }

+ 12 - 0
src/main/java/cz/senslog/telemetry/server/ParamParseException.java

@@ -0,0 +1,12 @@
+package cz.senslog.telemetry.server;
+
+public class ParamParseException extends IllegalArgumentException {
+
+    public ParamParseException(String message) {
+        super(message);
+    }
+
+    public ParamParseException(Throwable th) {
+        super(th);
+    }
+}