1
0

dividing api changes

This commit is contained in:
Jose
2025-03-13 21:00:17 +01:00
parent f00c9cdac0
commit 2ce90c5720
30 changed files with 581 additions and 707 deletions

View File

@@ -0,0 +1,8 @@
public class Main {
public static void main(String[] args) {
}
}

View File

@@ -9,7 +9,7 @@ public class ConfigManager {
private final Properties config; private final Properties config;
private ConfigManager() { private ConfigManager() {
this.configFile = new File(Constants.CONFIG_FILE); this.configFile = new File(Constants.CONFIG_FILE);
this.config = new Properties(); this.config = new Properties();
if (!configFile.exists()) { if (!configFile.exists()) {
@@ -23,6 +23,10 @@ public class ConfigManager {
loadConfig(); loadConfig();
} }
public static void init() {
ConfigManager.getInstance();
}
public static synchronized ConfigManager getInstance() { public static synchronized ConfigManager getInstance() {
if (instance == null) { if (instance == null) {
instance = new ConfigManager(); instance = new ConfigManager();

View File

@@ -2,48 +2,52 @@ package net.miarma.contaminus.common;
import io.vertx.core.impl.logging.Logger; import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.impl.logging.LoggerFactory;
import net.miarma.contaminus.util.SystemUtil;
public class Constants { public class Constants {
public static final String APP_NAME = "ContaminUS"; public static final String APP_NAME = "ContaminUS";
public static final String API_PREFIX = "/api/v1"; public static final int API_VERSION = 1;
public static final String HOME_DIR = SystemInfo.getOS() == OSType.WINDOWS ? public static final String API_PREFIX = "/api/v" + API_VERSION;
public static final String HOME_DIR = SystemUtil.getOS() == OSType.WINDOWS ?
"C:/Users/" + System.getProperty("user.name") + "/" : "C:/Users/" + System.getProperty("user.name") + "/" :
System.getProperty("user.home").contains("root") ? "/root/" : System.getProperty("user.home").contains("root") ? "/root/" :
"/home/" + System.getProperty("user.name") + "/"; "/home/" + System.getProperty("user.name") + "/";
public static final String BASE_DIR = HOME_DIR + public static final String BASE_DIR = HOME_DIR +
(SystemInfo.getOS() == OSType.WINDOWS ? ".contaminus" : (SystemUtil.getOS() == OSType.WINDOWS ? ".contaminus" :
SystemInfo.getOS() == OSType.LINUX ? ".config" + "/" + SystemUtil.getOS() == OSType.LINUX ? ".config/contaminus" :
"contaminus" : null); ".contaminus");
public static final String CONFIG_FILE = BASE_DIR + "/" + "config.properties"; public static final String CONFIG_FILE = BASE_DIR + "/" + "config.properties";
public static final Logger LOGGER = LoggerFactory.getLogger(APP_NAME); public static final Logger LOGGER = LoggerFactory.getLogger(APP_NAME);
/* API Endpoints */
public static final String GET_GROUPS = API_PREFIX + "/groups"; public static final String GET_GROUPS = API_PREFIX + "/groups";
public static final String GET_GROUP_BY_ID = API_PREFIX + "/groups/:groupId";
public static final String GET_GROUP_DEVICES = API_PREFIX + "/groups/:groupId/devices";
public static final String POST_GROUPS = API_PREFIX + "/groups"; public static final String POST_GROUPS = API_PREFIX + "/groups";
public static final String PUT_GROUP_BY_ID = API_PREFIX + "/groups/:groupId"; public static final String PUT_GROUP_BY_ID = API_PREFIX + "/groups/:groupId";
public static final String GET_DEVICES = API_PREFIX + "/devices"; public static final String GET_DEVICES = API_PREFIX + "/devices";
public static final String GET_DEVICE_BY_ID = API_PREFIX + "/devices/:deviceId";
public static final String GET_DEVICE_SENSORS = API_PREFIX + "/devices/:deviceId/sensors";
public static final String POST_DEVICES = API_PREFIX + "/devices"; public static final String POST_DEVICES = API_PREFIX + "/devices";
public static final String PUT_DEVICE_BY_ID = API_PREFIX + "/devices/:deviceId"; public static final String PUT_DEVICE_BY_ID = API_PREFIX + "/devices/:deviceId";
public static final String GET_DEVICE_ACTUATORS = API_PREFIX + "/devices/:deviceId/actuators";
public static final String GET_DEVICE_LATEST_VALUES = API_PREFIX + "/devices/:deviceId/latest";
public static final String GET_DEVICE_POLLUTION_MAP = API_PREFIX + "/devices/:deviceId/pollution-map";
public static final String GET_DEVICE_HISTORY = API_PREFIX + "/devices/:deviceId/history";
public static final String GET_SENSORS = API_PREFIX + "/sensors"; public static final String GET_SENSORS = API_PREFIX + "/sensors";
public static final String GET_SENSOR_BY_ID = API_PREFIX + "/sensors/:sensorId";
public static final String GET_SENSOR_VALUES = API_PREFIX + "/sensors/:sensorId/values";
public static final String POST_SENSORS = API_PREFIX + "/sensors"; public static final String POST_SENSORS = API_PREFIX + "/sensors";
public static final String PUT_SENSOR_BY_ID = API_PREFIX + "/sensors/:sensorId"; public static final String PUT_SENSOR_BY_ID = API_PREFIX + "/sensors/:sensorId";
public static final String GET_ACTUATORS = API_PREFIX + "/actuators"; public static final String GET_ACTUATORS = API_PREFIX + "/actuators";
public static final String GET_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId";
public static final String POST_ACTUATORS = API_PREFIX + "/actuators"; public static final String POST_ACTUATORS = API_PREFIX + "/actuators";
public static final String PUT_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId"; public static final String PUT_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId";
/* Bussiness Logic API */
public static final String GET_GROUP_BY_ID = API_PREFIX + "/groups/:groupId";
public static final String GET_GROUP_DEVICES = API_PREFIX + "/groups/:groupId/devices";
public static final String GET_DEVICE_BY_ID = API_PREFIX + "/devices/:deviceId";
public static final String GET_DEVICE_SENSORS = API_PREFIX + "/devices/:deviceId/sensors";
public static final String GET_DEVICE_ACTUATORS = API_PREFIX + "/devices/:deviceId/actuators";
public static final String GET_DEVICE_LATEST_VALUES = API_PREFIX + "/devices/:deviceId/latest";
public static final String GET_DEVICE_POLLUTION_MAP = API_PREFIX + "/devices/:deviceId/pollution-map";
public static final String GET_DEVICE_HISTORY = API_PREFIX + "/devices/:deviceId/history";
public static final String GET_SENSOR_BY_ID = API_PREFIX + "/sensors/:sensorId";
public static final String GET_SENSOR_VALUES = API_PREFIX + "/sensors/:sensorId/values";
public static final String GET_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId";
private Constants() { private Constants() {
throw new AssertionError("Utility class cannot be instantiated."); throw new AssertionError("Utility class cannot be instantiated.");

View File

@@ -1,21 +0,0 @@
package net.miarma.contaminus.common;
public class Host {
static ConfigManager configManager = ConfigManager.getInstance();
static String host = configManager.getStringProperty("inet.host");
static String origin = configManager.getStringProperty("inet.origin");
static int apiPort = configManager.getIntProperty("api.port");
static int webserverPort = configManager.getIntProperty("webserver.port");
public static String getHost() {
return host;
}
public static int getApiPort() {
return apiPort;
}
public static int getWebserverPort() {
return webserverPort;
}
}

View File

@@ -1,14 +0,0 @@
package net.miarma.contaminus.common;
public class SystemInfo {
public static OSType getOS() {
String envProperty = System.getProperty("os.name").toLowerCase();
if(envProperty.contains("windows")) {
return OSType.WINDOWS;
} else if(envProperty.contains("linux")) {
return OSType.LINUX;
} else {
return OSType.INVALID_OS;
}
}
}

View File

@@ -0,0 +1,12 @@
package net.miarma.contaminus.common;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE) // aplica a clases/interfaces
@Retention(RetentionPolicy.RUNTIME) // disponible en tiempo de ejecución
public @interface Table {
String value();
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class Actuator {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class COValue {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class Device {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class GpsValue {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class Group {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class Sensor {
}

View File

@@ -1,5 +0,0 @@
package net.miarma.contaminus.common.entities;
public class WeatherValue {
}

View File

@@ -1,30 +1,68 @@
package net.miarma.contaminus.database; package net.miarma.contaminus.database;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject; import io.vertx.jdbcclient.JDBCConnectOptions;
import io.vertx.jdbcclient.JDBCPool; import io.vertx.jdbcclient.JDBCPool;
import io.vertx.sqlclient.PoolOptions;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
import net.miarma.contaminus.common.ConfigManager; import net.miarma.contaminus.common.ConfigManager;
import net.miarma.contaminus.common.Constants;
public class DatabaseManager { public class DatabaseManager {
private final JDBCPool pool; private final JDBCPool pool;
@SuppressWarnings("deprecation")
public DatabaseManager(Vertx vertx) { public DatabaseManager(Vertx vertx) {
ConfigManager config = ConfigManager.getInstance(); ConfigManager config = ConfigManager.getInstance();
JsonObject dbConfig = new JsonObject() JDBCConnectOptions jdbcOptions = new JDBCConnectOptions()
.put("url", config.getStringProperty("db.protocol") + "//" + .setJdbcUrl(config.getStringProperty("db.protocol") + "//" +
config.getStringProperty("db.host") + ":" + config.getStringProperty("db.host") + ":" +
config.getStringProperty("db.port") + "/" + config.getStringProperty("db.port") + "/" +
config.getStringProperty("db.name")) config.getStringProperty("db.name"))
.put("user", config.getStringProperty("db.user")) .setUser(config.getStringProperty("db.user"))
.put("password", config.getStringProperty("db.pwd")) .setPassword(config.getStringProperty("db.pwd"));
.put("max_pool_size", config.getStringProperty("db.poolSize"));
pool = JDBCPool.pool(vertx, dbConfig); PoolOptions poolOptions = new PoolOptions()
.setMaxSize(Integer.parseInt(config.getStringProperty("db.poolSize")));
pool = JDBCPool.pool(vertx, jdbcOptions, poolOptions);
} }
public JDBCPool getPool() { public Future<RowSet<Row>> testConnection() {
return pool; return pool.query("SELECT 1").execute();
} }
public <T> Future<List<T>> execute(String query, Class<T> clazz,
Handler<List<T>> onSuccess, Handler<Throwable> onFailure) {
return pool.query(query).execute()
.map(rows -> {
List<T> results = new ArrayList<>();
for (Row row : rows) {
try {
Constructor<T> constructor = clazz.getConstructor(Row.class);
results.add(constructor.newInstance(row));
} catch (NoSuchMethodException | InstantiationException |
IllegalAccessException | InvocationTargetException e) {
Constants.LOGGER.error("Error instantiating class: " + e.getMessage());
}
}
return results;
})
.onComplete(ar -> {
if (ar.succeeded()) {
onSuccess.handle(ar.result());
} else {
onFailure.handle(ar.cause());
}
});
}
} }

View File

@@ -1,87 +1,135 @@
package net.miarma.contaminus.database; package net.miarma.contaminus.database;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.StringJoiner; import java.util.StringJoiner;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.Table;
public class QueryBuilder { public class QueryBuilder {
private StringBuilder query; private StringBuilder query;
private List<String> conditions;
private String sort; private String sort;
private String order; private String order;
private String limit; private String limit;
public QueryBuilder() { public QueryBuilder() {
this.query = new StringBuilder(); this.query = new StringBuilder();
this.conditions = new ArrayList<>();
} }
public static QueryBuilder select(String... columns) { private static <T> String getTableName(Class<T> clazz) {
QueryBuilder qb = new QueryBuilder(); if (clazz.isAnnotationPresent(Table.class)) {
StringJoiner joiner = new StringJoiner(", "); Table annotation = clazz.getAnnotation(Table.class);
for (String column : columns) { return annotation.value(); // lee el nombre de la tabla desde la annotation
joiner.add(column);
} }
qb.query.append("SELECT ").append(joiner).append(" "); throw new IllegalArgumentException("Clase no tiene la anotación @TableName");
}
public String getQuery() {
return query.toString();
}
public static <T> QueryBuilder select(Class<T> clazz, String... columns) {
QueryBuilder qb = new QueryBuilder();
String tableName = getTableName(clazz);
qb.query.append("SELECT ");
if (columns.length == 0) {
qb.query.append("* ");
} else {
StringJoiner joiner = new StringJoiner(", ");
for (String column : columns) {
joiner.add(column);
}
qb.query.append(joiner).append(" ");
}
qb.query.append("FROM ").append(tableName).append(" ");
return qb; return qb;
} }
public static QueryBuilder insert(String table, String... columns) { public static <T> QueryBuilder where(QueryBuilder qb, T object) {
QueryBuilder qb = new QueryBuilder(); List<String> conditions = new ArrayList<>();
StringJoiner joiner = new StringJoiner(", "); Class<?> clazz = object.getClass();
if (columns.length > 0) {
for (String column : columns) {
joiner.add(column);
}
qb.query.append("INSERT INTO ").append(table).append(" (").append(joiner).append(") ");
} else {
qb.query.append("INSERT INTO ").append(table).append(" ");
}
return qb; for (Field field : clazz.getDeclaredFields()) {
} field.setAccessible(true);
try {
public static QueryBuilder update(String table) { Object value = field.get(object);
QueryBuilder qb = new QueryBuilder(); if (value != null) {
qb.query.append("UPDATE ").append(table).append(" "); if (value instanceof String) {
return qb; conditions.add(field.getName() + " = '" + value + "'");
} } else {
conditions.add(field.getName() + " = " + value);
public QueryBuilder set(String column, Object value) { }
if(value instanceof String) { }
query.append("SET ").append(column).append(" = '").append(value).append("' "); } catch (IllegalAccessException e) {
} else { Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
query.append("SET ").append(column).append(" = ").append(value).append(" ");
}
return this;
}
public QueryBuilder values(Object... values) {
StringJoiner joiner = new StringJoiner(", ", "VALUES (", ")");
for (Object value : values) {
if(value instanceof String) {
joiner.add("'" + value + "'");
} else if(value == null) {
joiner.add("NULL");
}
else {
joiner.add(value.toString());
} }
} }
this.query.append(joiner).append(" ");
return this; if (!conditions.isEmpty()) {
qb.query.append("WHERE ").append(String.join(" AND ", conditions)).append(" ");
}
return qb;
} }
public QueryBuilder from(String table) { public static <T> QueryBuilder select(T object, String... columns) {
query.append("FROM ").append(table).append(" "); Class<?> clazz = object.getClass();
return this; QueryBuilder qb = select(clazz, columns);
return where(qb, object);
} }
public QueryBuilder where(String conditionsString, Object... values) {
conditionsString = conditionsString.replaceAll("\\?", "%s"); public static <T> QueryBuilder insert(T object) {
conditions.add(String.format(conditionsString, values)); QueryBuilder qb = new QueryBuilder();
return this; String table = getTableName(object.getClass());
qb.query.append("INSERT INTO ").append(table).append(" ");
qb.query.append("(");
StringJoiner columns = new StringJoiner(", ");
StringJoiner values = new StringJoiner(", ");
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
columns.add(field.getName());
if(field.get(object) instanceof String) {
values.add("'" + field.get(object) + "'");
} else {
values.add(field.get(object).toString());
}
} catch (IllegalArgumentException | IllegalAccessException e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
}
}
qb.query.append(columns).append(") ");
qb.query.append("VALUES (").append(values).append(") ");
return qb;
}
public static <T> QueryBuilder update(T object) {
QueryBuilder qb = new QueryBuilder();
String table = getTableName(object.getClass());
qb.query.append("UPDATE ").append(table).append(" ");
qb.query.append("SET ");
StringJoiner joiner = new StringJoiner(", ");
for (Field field : object.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
if(field.get(object) instanceof String) {
joiner.add(field.getName() + " = '" + field.get(object) + "'");
} else {
joiner.add(field.getName() + " = " + field.get(object).toString());
}
} catch (IllegalArgumentException | IllegalAccessException e) {
Constants.LOGGER.error("(REFLECTION) Error reading field: " + e.getMessage());
}
}
qb.query.append(joiner).append(" ");
return qb;
} }
public QueryBuilder orderBy(Optional<String> column, Optional<String> order) { public QueryBuilder orderBy(Optional<String> column, Optional<String> order) {
@@ -100,14 +148,6 @@ public class QueryBuilder {
} }
public String build() { public String build() {
if (!conditions.isEmpty()) {
query.append("WHERE ");
StringJoiner joiner = new StringJoiner(" AND ");
for (String condition : conditions) {
joiner.add(condition);
}
query.append(joiner).append(" ");
}
if (order != null && !order.isEmpty()) { if (order != null && !order.isEmpty()) {
query.append(order); query.append(order);
} }

View File

@@ -0,0 +1,5 @@
package net.miarma.contaminus.database.entities;
public class Actuator {
}

View File

@@ -0,0 +1,5 @@
package net.miarma.contaminus.database.entities;
public class COValue {
}

View File

@@ -0,0 +1,5 @@
package net.miarma.contaminus.database.entities;
public class Device {
}

View File

@@ -0,0 +1,5 @@
package net.miarma.contaminus.database.entities;
public class GpsValue {
}

View File

@@ -0,0 +1,17 @@
package net.miarma.contaminus.database.entities;
import net.miarma.contaminus.common.Table;
@Table("groups")
@SuppressWarnings("unused")
public class Group {
private int groupId;
private String groupName;
public Group() {}
public Group(int groupId, String groupName) {
this.groupId = groupId;
this.groupName = groupName;
}
}

View File

@@ -0,0 +1,5 @@
package net.miarma.contaminus.database.entities;
public class Sensor {
}

View File

@@ -0,0 +1,5 @@
package net.miarma.contaminus.database.entities;
public class WeatherValue {
}

View File

@@ -1,403 +0,0 @@
package net.miarma.contaminus.server;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.Message;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.Host;
import net.miarma.contaminus.database.QueryBuilder;
public class ApiVerticle extends AbstractVerticle {
@Override
public void start(Promise<Void> startPromise) {
Constants.LOGGER.info("🟢 Iniciando ApiVerticle...");
Router router = Router.router(vertx);
Set<HttpMethod> allowedMethods = new HashSet<>(
Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.OPTIONS)); // Por ejemplo
Set<String> allowedHeaders = new HashSet<>(Arrays.asList("Content-Type", "Authorization"));
router.route().handler(CorsHandler.create()
.allowCredentials(true)
.allowedHeaders(allowedHeaders)
.allowedMethods(allowedMethods));
router.route().handler(BodyHandler.create());
// Group Routes
router.route(HttpMethod.GET, Constants.GET_GROUPS).handler(this::getGroupsHandler);
router.route(HttpMethod.GET, Constants.GET_GROUP_BY_ID).handler(this::getGroupByIdHandler);
router.route(HttpMethod.GET, Constants.GET_GROUP_DEVICES).handler(this::getGroupDevicesHandler);
router.route(HttpMethod.POST, Constants.POST_GROUPS).handler(this::postGroupHandler);
router.route(HttpMethod.PUT, Constants.PUT_GROUP_BY_ID).handler(this::putGroupByIdHandler);
// Device Routes
router.route(HttpMethod.GET, Constants.GET_DEVICES).handler(this::getDevicesHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_BY_ID).handler(this::getDeviceByIdHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_SENSORS).handler(this::getDeviceSensorsHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_ACTUATORS).handler(this::getDeviceActuatorsHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_LATEST_VALUES).handler(this::getDeviceLatestValuesHandler);
router.route(HttpMethod.POST, Constants.POST_DEVICES).handler(this::postDeviceHandler);
router.route(HttpMethod.PUT, Constants.PUT_DEVICE_BY_ID).handler(this::putDeviceByIdHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_POLLUTION_MAP).handler(this::getPollutionMapHandler);
router.route(HttpMethod.GET, Constants.GET_DEVICE_HISTORY).handler(this::getDeviceHistoryHandler);
// Sensor Routes
router.route(HttpMethod.GET, Constants.GET_SENSORS).handler(this::getSensorsHandler);
router.route(HttpMethod.GET, Constants.GET_SENSOR_BY_ID).handler(this::getSensorByIdHandler);
router.route(HttpMethod.GET, Constants.GET_SENSOR_VALUES).handler(this::getSensorValuesHandler);
router.route(HttpMethod.POST, Constants.POST_SENSORS).handler(this::postSensorHandler);
router.route(HttpMethod.PUT, Constants.PUT_SENSOR_BY_ID).handler(this::putSensorByIdHandler);
// Actuator Routes
router.route(HttpMethod.GET, Constants.GET_ACTUATORS).handler(this::getActuatorsHandler);
router.route(HttpMethod.GET, Constants.GET_ACTUATOR_BY_ID).handler(this::getActuatorByIdHandler);
router.route(HttpMethod.POST, Constants.POST_ACTUATORS).handler(this::postActuatorHandler);
router.route(HttpMethod.PUT, Constants.PUT_ACTUATOR_BY_ID).handler(this::putActuatorByIdHandler);
vertx.createHttpServer()
.requestHandler(router)
.listen(
Host.getApiPort(),
Host.getHost(),
result -> {
if (result.succeeded()) {
Constants.LOGGER.info(String.format(
"📡 ApiVerticle desplegado. (http://%s:%d)", Host.getHost(), Host.getApiPort()
));
startPromise.complete();
} else {
Constants.LOGGER.error("❌ Error al desplegar ApiVerticle", result.cause());
startPromise.fail(result.cause());
}
});
}
private void sendQuery(String query, RoutingContext context) {
vertx.eventBus().request("db.query", query, req -> {
if (req.succeeded()) {
Message<Object> msg = req.result();
JsonArray jsonArray = new JsonArray(msg.body().toString());
context.json(jsonArray);
} else {
context.fail(500, req.cause());
}
});
}
// Group Handlers
private void getGroupsHandler(RoutingContext context) {
String query = QueryBuilder.select("*").from("groups").build();
sendQuery(query, context);
}
private void getGroupByIdHandler(RoutingContext context) {
String groupId = context.request().getParam("groupId");
String query = QueryBuilder
.select("*")
.from("groups")
.where("groupId = ?", groupId)
.build();
sendQuery(query, context);
}
private void getGroupDevicesHandler(RoutingContext context) {
String groupId = context.request().getParam("groupId");
String query = QueryBuilder
.select("*")
.from("devices")
.where("groupId = ?", groupId)
.build();
sendQuery(query, context);
}
private void postGroupHandler(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
String groupName = body.getString("groupName");
String query = QueryBuilder
.insert("groups", "groupName")
.values(groupName)
.build();
sendQuery(query, context);
}
private void putGroupByIdHandler(RoutingContext context) {
String groupId = context.request().getParam("groupId");
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
String groupName = body.getString("groupName");
String query = QueryBuilder
.update("groups")
.set("groupName", groupName)
.where("groupId = ?", groupId)
.build();
sendQuery(query, context);
}
// Device Handlers
private void getDevicesHandler(RoutingContext context) {
String query = QueryBuilder.select("*").from("devices").build();
sendQuery(query, context);
}
private void getDeviceByIdHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("devices")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void getDeviceSensorsHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("sensors")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void getDeviceActuatorsHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("actuators")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void getDeviceLatestValuesHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("v_latest_values")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void postDeviceHandler(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer groupId = body.getInteger("groupId");
String deviceName = body.getString("groupName");
String query = QueryBuilder
.insert("devices", "groupId", "deviceName")
.values(groupId, deviceName)
.build();
sendQuery(query, context);
}
private void putDeviceByIdHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer groupId = body.getInteger("groupId");
String deviceName = body.getString("deviceName");
String query = QueryBuilder
.update("devices")
.set("groupId", groupId)
.set("deviceName", deviceName)
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void getPollutionMapHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("v_pollution_map")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
private void getDeviceHistoryHandler(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
String query = QueryBuilder
.select("*")
.from("v_sensor_history_by_device")
.where("deviceId = ?", deviceId)
.build();
sendQuery(query, context);
}
// Sensor Handlers
private void getSensorsHandler(RoutingContext context) {
String query = QueryBuilder.select("*").from("sensors").build();
sendQuery(query, context);
}
private void getSensorByIdHandler(RoutingContext context) {
String sensorId = context.request().getParam("sensorId");
String query = QueryBuilder
.select("*")
.from("sensors")
.where("sensorId = ?", sensorId)
.build();
sendQuery(query, context);
}
private void getSensorValuesHandler(RoutingContext context) {
String sensorId = context.request().getParam("sensorId");
String query = QueryBuilder
.select("*")
.from("v_sensor_values")
.where("sensorId = ?", sensorId)
.build();
sendQuery(query, context);
}
private void postSensorHandler(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer deviceId = body.getInteger("deviceId");
String sensorType = body.getString("sensorType");
String unit = body.getString("unit");
Integer status = body.getInteger("status");
String query = QueryBuilder
.insert("sensors", "deviceId", "sensorType", "unit", "status")
.values(deviceId, sensorType, unit, status)
.build();
sendQuery(query, context);
}
private void putSensorByIdHandler(RoutingContext context) {
String sensorId = context.request().getParam("sensorId");
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer deviceId = body.getInteger("deviceId");
String sensorType = body.getString("sensorType");
String unit = body.getString("unit");
Integer status = body.getInteger("status");
String query = QueryBuilder
.update("sensors")
.set("deviceId", deviceId)
.set("sensorType", sensorType)
.set("unit", unit)
.set("status", status)
.where("sensorId = ?", sensorId)
.build();
sendQuery(query, context);
}
// Actuator Handlers
private void getActuatorsHandler(RoutingContext context) {
String query = QueryBuilder.select("*").from("actuators").build();
sendQuery(query, context);
}
private void getActuatorByIdHandler(RoutingContext context) {
String actuatorId = context.request().getParam("actuatorId");
String query = QueryBuilder
.select("*")
.from("actuators")
.where("actuatorId = ?", actuatorId)
.build();
sendQuery(query, context);
}
private void postActuatorHandler(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer deviceId = body.getInteger("deviceId");
Integer status = body.getInteger("status");
String query = QueryBuilder
.insert("actuators", "deviceId", "status")
.values(deviceId, status)
.build();
sendQuery(query, context);
}
private void putActuatorByIdHandler(RoutingContext context) {
String actuatorId = context.request().getParam("actuatorId");
JsonObject body = context.body().asJsonObject();
if(body == null) {
context.fail(400, new IllegalArgumentException("Bad request"));
return;
}
Integer deviceId = body.getInteger("deviceId");
Integer status = body.getInteger("status");
String query = QueryBuilder
.update("actuators")
.set("deviceId", deviceId)
.set("status", status)
.where("actuatorId = ?", actuatorId)
.build();
sendQuery(query, context);
}
}

View File

@@ -0,0 +1,164 @@
package net.miarma.contaminus.server;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import net.miarma.contaminus.common.ConfigManager;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.database.DatabaseManager;
import net.miarma.contaminus.util.SystemUtil;
@SuppressWarnings("unused")
public class DataLayerAPIVerticle extends AbstractVerticle {
private DatabaseManager dbManager;
private Gson gson;
private ConfigManager configManager;
@Override
public void start(Promise<Void> startPromise) {
Constants.LOGGER.info("📡 Iniciando DataLayerAPIVerticle...");
configManager = ConfigManager.getInstance();
gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
dbManager = new DatabaseManager(vertx);
Router router = Router.router(vertx);
Set<HttpMethod> allowedMethods = new HashSet<>(
Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.OPTIONS)); // Por ejemplo
Set<String> allowedHeaders = new HashSet<>(Arrays.asList("Content-Type", "Authorization"));
router.route().handler(CorsHandler.create()
.allowCredentials(true)
.allowedHeaders(allowedHeaders)
.allowedMethods(allowedMethods));
router.route().handler(BodyHandler.create());
// Group Routes
router.route(HttpMethod.GET, Constants.GET_GROUPS).handler(this::getAllGroups);
router.route(HttpMethod.GET, Constants.GET_GROUP_BY_ID).handler(this::getGroupById);
router.route(HttpMethod.POST, Constants.POST_GROUPS).handler(this::addGroup);
router.route(HttpMethod.PUT, Constants.PUT_GROUP_BY_ID).handler(this::updateGroup);
// Device Routes
router.route(HttpMethod.GET, Constants.GET_DEVICES).handler(this::getAllDevices);
router.route(HttpMethod.GET, Constants.GET_DEVICE_BY_ID).handler(this::getDeviceById);
router.route(HttpMethod.POST, Constants.POST_DEVICES).handler(this::addDevice);
router.route(HttpMethod.PUT, Constants.PUT_DEVICE_BY_ID).handler(this::updateDevice);
// Sensor Routes
router.route(HttpMethod.GET, Constants.GET_SENSORS).handler(this::getAllSensors);
router.route(HttpMethod.GET, Constants.GET_SENSOR_BY_ID).handler(this::getSensorById);
router.route(HttpMethod.POST, Constants.POST_SENSORS).handler(this::addSensor);
router.route(HttpMethod.PUT, Constants.PUT_SENSOR_BY_ID).handler(this::updateSensor);
// Actuator Routes
router.route(HttpMethod.GET, Constants.GET_ACTUATORS).handler(this::getAllActuators);
router.route(HttpMethod.GET, Constants.GET_ACTUATOR_BY_ID).handler(this::getActuatorById);
router.route(HttpMethod.POST, Constants.POST_ACTUATORS).handler(this::addActuator);
router.route(HttpMethod.PUT, Constants.PUT_ACTUATOR_BY_ID).handler(this::updateActuator);
dbManager.testConnection()
.onSuccess(rows -> {
Constants.LOGGER.info("✅ Database connection ok");
Constants.LOGGER.info("🟢 DataAccessVerticle desplegado");
startPromise.complete();
})
.onFailure(onFailure -> {
Constants.LOGGER.error("❌ Database connection failed");
Constants.LOGGER.error("🔴 Error al desplegar DataAccessVerticle", onFailure);
startPromise.fail(onFailure);
});
vertx.createHttpServer()
.requestHandler(router)
.listen(SystemUtil.getDataApiPort(), result -> {
if (result.succeeded()) {
Constants.LOGGER.info(String.format(
"🟢 DataLayerAPIVerticle desplegado. (http://%s:%d)",
SystemUtil.getHost(), SystemUtil.getDataApiPort()
));
startPromise.complete();
} else {
Constants.LOGGER.error("🔴 Error al desplegar DataLayerAPIVerticle", result.cause());
startPromise.fail(result.cause());
}
});
}
private void getAllGroups(RoutingContext context) {
context.response().end("TODO");
}
private void getGroupById(RoutingContext context) {
context.response().end("TODO");
}
private void addGroup(RoutingContext context) {
context.response().end("TODO");
}
private void updateGroup(RoutingContext context) {
context.response().end("TODO");
}
private void getAllDevices(RoutingContext context) {
context.response().end("TODO");
}
private void getDeviceById(RoutingContext context) {
context.response().end("TODO");
}
private void addDevice(RoutingContext context) {
context.response().end("TODO");
}
private void updateDevice(RoutingContext context) {
context.response().end("TODO");
}
private void getAllSensors(RoutingContext context) {
context.response().end("TODO");
}
private void getSensorById(RoutingContext context) {
context.response().end("TODO");
}
private void addSensor(RoutingContext context) {
context.response().end("TODO");
}
private void updateSensor(RoutingContext context) {
context.response().end("TODO");
}
private void getAllActuators(RoutingContext context) {
context.response().end("TODO");
}
private void getActuatorById(RoutingContext context) {
context.response().end("TODO");
}
private void addActuator(RoutingContext context) {
context.response().end("TODO");
}
private void updateActuator(RoutingContext context) {
context.response().end("TODO");
}
}

View File

@@ -1,120 +0,0 @@
package net.miarma.contaminus.server;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonObject;
import io.vertx.jdbcclient.JDBCPool;
import io.vertx.sqlclient.Row;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.LocalDateTimeSerializer;
import net.miarma.contaminus.database.DatabaseManager;
public class DatabaseVerticle extends AbstractVerticle {
private JDBCPool pool;
private EventBus eventBus;
private Gson gson = new GsonBuilder()
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer())
.create();
@SuppressWarnings("unused")
@Override
public void start(Promise<Void> startPromise) {
Constants.LOGGER.info("🟢 Iniciando DatabaseVerticle...");
DatabaseManager dbManager = new DatabaseManager(vertx);
pool = dbManager.getPool();
eventBus = vertx.eventBus();
pool.query("SELECT 1")
.execute()
.onSuccess(_res -> {
Constants.LOGGER.info("✅ Database connection ok");
Constants.LOGGER.info("📡 DatabaseVerticle desplegado");
startPromise.complete();
})
.onFailure(err -> {
Constants.LOGGER.error("❌ Database connection failed");
Constants.LOGGER.error("❌ Error al desplegar DatabaseVerticle", err);
startPromise.fail(err);
});
eventBus.consumer("db.query", this::handleDatabaseQuery);
}
private void handleDatabaseQuery(Message<String> msg) {
String query = msg.body();
Constants.LOGGER.info("📥 Query: " + query);
if(query == null || query.isEmpty()) {
msg.fail(400, "Empty query");
return;
}
if(query.startsWith("SELECT")) {
handleSelectQuery(query, msg);
} else if(query.startsWith("INSERT")) {
handleInsertQuery(query, msg);
} else if(query.startsWith("UPDATE")) {
handleUpdateQuery(query, msg);
} else {
msg.fail(400, "Invalid operation");
}
}
private void handleSelectQuery(String query, Message<String> msg) {
pool.query(query).execute()
.onSuccess(res -> {
List<Map<String, Object>> rowsList = new ArrayList<>();
for (Row row : res) {
Map<String, Object> rowMap = new HashMap<>();
for (int i = 0; i < row.size(); i++) {
String columnName = res.columnsNames().get(i);
Object columnValue = row.getValue(i);
rowMap.put(columnName, columnValue);
}
rowsList.add(rowMap);
}
String jsonResponse = gson.toJson(rowsList);
msg.reply(jsonResponse);
})
.onFailure(err -> msg.fail(500, err.getMessage()));
}
@SuppressWarnings("unused")
private void handleInsertQuery(String query, Message<String> msg) {
pool.query(query).execute()
.onSuccess(_res -> {
JsonObject response = new JsonObject();
response.put("status", "success");
String jsonResponse = gson.toJson(response);
msg.reply(jsonResponse);
})
.onFailure(err -> msg.fail(500, err.getMessage()));
}
@SuppressWarnings("unused")
private void handleUpdateQuery(String query, Message<String> msg) {
pool.query(query).execute()
.onSuccess(_res -> {
JsonObject response = new JsonObject();
response.put("status", "updated");
String jsonResponse = gson.toJson(response);
msg.reply(jsonResponse);
})
.onFailure(err -> msg.fail(500, err.getMessage()));
}
}

View File

@@ -4,13 +4,13 @@ import io.vertx.core.AbstractVerticle;
import io.vertx.ext.web.Router; import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.StaticHandler; import io.vertx.ext.web.handler.StaticHandler;
import net.miarma.contaminus.common.Constants; import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.common.Host; import net.miarma.contaminus.util.SystemUtil;
public class HttpServerVerticle extends AbstractVerticle { public class HttpServerVerticle extends AbstractVerticle {
@Override @Override
public void start() { public void start() {
Constants.LOGGER.info("🟢 Iniciando HttpServerVerticle..."); Constants.LOGGER.info("📡 Iniciando HttpServerVerticle...");
Router router = Router.router(vertx); Router router = Router.router(vertx);
@@ -25,13 +25,13 @@ public class HttpServerVerticle extends AbstractVerticle {
}); });
vertx.createHttpServer().requestHandler(router).listen( vertx.createHttpServer().requestHandler(router).listen(
Host.getWebserverPort(), Host.getHost(), result -> { SystemUtil.getWebserverPort(), SystemUtil.getHost(), result -> {
if (result.succeeded()) { if (result.succeeded()) {
Constants.LOGGER.info(String.format("📡 HttpServerVerticle desplegado. (http://%s:%d)", Constants.LOGGER.info(String.format("🟢 HttpServerVerticle desplegado. (http://%s:%d)",
Host.getHost(), Host.getWebserverPort()) SystemUtil.getHost(), SystemUtil.getWebserverPort())
); );
} else { } else {
Constants.LOGGER.error(" Error al desplegar HttpServerVerticle", result.cause()); Constants.LOGGER.error("🔴 Error al desplegar HttpServerVerticle", result.cause());
} }
} }
); );

View File

@@ -0,0 +1,108 @@
package net.miarma.contaminus.server;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import net.miarma.contaminus.common.ConfigManager;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.database.DatabaseManager;
import net.miarma.contaminus.util.SystemUtil;
@SuppressWarnings("unused")
public class LogicLayerAPIVerticle extends AbstractVerticle {
private DatabaseManager dbManager;
private Gson gson;
private ConfigManager configManager;
@Override
public void start(Promise<Void> startPromise) {
Constants.LOGGER.info("📡 Iniciando LogicApiVerticle...");
configManager = ConfigManager.getInstance();
gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
dbManager = new DatabaseManager(vertx);
Router router = Router.router(vertx);
Set<HttpMethod> allowedMethods = new HashSet<>(
Arrays.asList(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.OPTIONS)); // Por ejemplo
Set<String> allowedHeaders = new HashSet<>(Arrays.asList("Content-Type", "Authorization"));
router.route().handler(CorsHandler.create()
.allowCredentials(true)
.allowedHeaders(allowedHeaders)
.allowedMethods(allowedMethods));
router.route().handler(BodyHandler.create());
// Group Routes
router.route(HttpMethod.GET, Constants.GET_GROUP_DEVICES).handler(this::getGroupDevices);
// Device Routes
router.route(HttpMethod.GET, Constants.GET_DEVICE_SENSORS).handler(this::getDeviceSensors);
router.route(HttpMethod.GET, Constants.GET_DEVICE_ACTUATORS).handler(this::getDeviceActuators);
router.route(HttpMethod.GET, Constants.GET_DEVICE_LATEST_VALUES).handler(this::getDeviceLatestValues);
router.route(HttpMethod.GET, Constants.GET_DEVICE_POLLUTION_MAP).handler(this::getDevicePollutionMap);
router.route(HttpMethod.GET, Constants.GET_DEVICE_HISTORY).handler(this::getDeviceHistory);
// Sensor Routes
router.route(HttpMethod.GET, Constants.GET_SENSOR_VALUES).handler(this::getSensorValues);
vertx.createHttpServer()
.requestHandler(router)
.listen(
SystemUtil.getLogicApiPort(),
SystemUtil.getHost(),
result -> {
if (result.succeeded()) {
Constants.LOGGER.info(String.format(
"🟢 ApiVerticle desplegado. (http://%s:%d)", SystemUtil.getHost(), SystemUtil.getLogicApiPort()
));
startPromise.complete();
} else {
Constants.LOGGER.error("🔴 Error al desplegar LogicApiVerticle", result.cause());
startPromise.fail(result.cause());
}
});
}
private void getGroupDevices(RoutingContext context) {
context.response().end("TODO");
}
private void getDeviceSensors(RoutingContext context) {
context.response().end("TODO");
}
private void getDeviceActuators(RoutingContext context) {
context.response().end("TODO");
}
private void getDeviceLatestValues(RoutingContext context) {
context.response().end("TODO");
}
private void getDevicePollutionMap(RoutingContext context) {
context.response().end("TODO");
}
private void getDeviceHistory(RoutingContext context) {
context.response().end("TODO");
}
private void getSensorValues(RoutingContext context) {
context.response().end("TODO");
}
}

View File

@@ -14,13 +14,13 @@ public class MainVerticle extends AbstractVerticle {
String enabledVerticles = System.getProperty("vertx.options", ""); String enabledVerticles = System.getProperty("vertx.options", "");
if (enabledVerticles.contains("db")) { if (enabledVerticles.contains("data")) {
getVertx().deployVerticle(new DatabaseVerticle(), options); getVertx().deployVerticle(new DataLayerAPIVerticle(), options);
} }
if (enabledVerticles.contains("api")) { if (enabledVerticles.contains("business")) {
getVertx().deployVerticle(new ApiVerticle(), options); getVertx().deployVerticle(new LogicLayerAPIVerticle(), options);
} }
if (enabledVerticles.contains("http")) { if (enabledVerticles.contains("web")) {
getVertx().deployVerticle(new HttpServerVerticle()); getVertx().deployVerticle(new HttpServerVerticle());
} }
} }

View File

@@ -0,0 +1,41 @@
package net.miarma.contaminus.util;
import net.miarma.contaminus.common.ConfigManager;
import net.miarma.contaminus.common.OSType;
public class SystemUtil {
static ConfigManager configManager = ConfigManager.getInstance();
static String host = configManager.getStringProperty("inet.host");
static String origin = configManager.getStringProperty("inet.origin");
static int dataApiPort = configManager.getIntProperty("data-api.port");
static int logicApiPort = configManager.getIntProperty("logic-api.port");
static int webserverPort = configManager.getIntProperty("webserver.port");
public static String getHost() {
return host;
}
public static int getDataApiPort() {
return dataApiPort;
}
public static int getLogicApiPort() {
return logicApiPort;
}
public static int getWebserverPort() {
return webserverPort;
}
public static OSType getOS() {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
return OSType.WINDOWS;
} else if (os.contains("nix") || os.contains("nux")) {
return OSType.LINUX;
} else {
return OSType.INVALID_OS;
}
}
}

View File

@@ -7,7 +7,8 @@ db.user=root
db.pwd=root db.pwd=root
dp.poolSize=5 dp.poolSize=5
# Server Configuration # HTTP Server Configuration
inet.host=localhost inet.host=localhost
webserver.port=8080 webserver.port=8080
api.port=8081 data-api.port=8081
logic-api.port=8082