Integrated MQTT clients
This commit is contained in:
@@ -28,26 +28,36 @@ repositories {
|
||||
extra["snippetsDir"] = file("build/generated-snippets")
|
||||
|
||||
dependencies {
|
||||
implementation("org.springframework.boot:spring-boot-security")
|
||||
|
||||
//======================MAIN======================
|
||||
implementation("org.springframework.boot:spring-boot-security")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-elasticsearch")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
|
||||
implementation("org.springframework.boot:spring-boot-starter-elasticsearch")
|
||||
implementation("org.springframework.boot:spring-boot-starter-mongodb")
|
||||
implementation("org.springframework.boot:spring-boot-starter-opentelemetry")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
|
||||
|
||||
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0")
|
||||
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
developmentOnly("org.springframework.boot:spring-boot-docker-compose")
|
||||
|
||||
runtimeOnly("io.micrometer:micrometer-registry-prometheus")
|
||||
|
||||
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||
|
||||
//======================ELASTIC SEARCH======================
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-elasticsearch")
|
||||
implementation("org.springframework.boot:spring-boot-starter-elasticsearch")
|
||||
|
||||
//======================MONGO DB======================
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
|
||||
implementation("org.springframework.boot:spring-boot-starter-mongodb")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
|
||||
|
||||
//======================SWAGGER======================
|
||||
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0")
|
||||
|
||||
//======================MQTT======================
|
||||
implementation("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5")
|
||||
|
||||
//======================PROMETHEUS======================
|
||||
runtimeOnly("io.micrometer:micrometer-registry-prometheus")
|
||||
|
||||
//======================OTHER======================
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
developmentOnly("org.springframework.boot:spring-boot-docker-compose")
|
||||
|
||||
testImplementation("org.springframework.boot:spring-boot-restdocs")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-actuator-test")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-data-elasticsearch-test")
|
||||
|
||||
@@ -5,4 +5,4 @@ scrape_configs:
|
||||
- job_name: 'spring-api'
|
||||
metrics_path: '/actuator/prometheus'
|
||||
static_configs:
|
||||
- targets: ['boardmate-api:8000']
|
||||
- targets: ['boardmate-api:8080']
|
||||
@@ -0,0 +1,11 @@
|
||||
package be.naaturel.boardmateapi.common.exceptions;
|
||||
|
||||
public class ServiceException extends Exception{
|
||||
public ServiceException(String message, Exception innerException){
|
||||
super(message, innerException);
|
||||
}
|
||||
|
||||
public ServiceException(String message){
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,10 @@ import be.naaturel.boardmateapi.common.helpers.Logger;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Configuration
|
||||
public class Startup {
|
||||
public class StartupLogger {
|
||||
|
||||
@Bean
|
||||
public String log() {
|
||||
@@ -1,10 +0,0 @@
|
||||
package be.naaturel.boardmateapi.configurations;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
|
||||
@Configuration
|
||||
public class SwaggerConfig {
|
||||
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package be.naaturel.boardmateapi.configurations;
|
||||
package be.naaturel.boardmateapi.configurations.configurations;
|
||||
|
||||
import be.naaturel.boardmateapi.configurations.properties.AppProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
@@ -18,12 +18,12 @@ import java.util.Arrays;
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableTransactionManagement
|
||||
public class AppSecurity {
|
||||
public class AppSecurityConfig {
|
||||
|
||||
private final AppConfigurations conf;
|
||||
private final AppProperties conf;
|
||||
|
||||
@Autowired
|
||||
public AppSecurity(AppConfigurations appConf) {
|
||||
public AppSecurityConfig(AppProperties appConf) {
|
||||
this.conf = appConf;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package be.naaturel.boardmateapi.configurations;
|
||||
package be.naaturel.boardmateapi.configurations.configurations;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
@@ -1,5 +1,6 @@
|
||||
package be.naaturel.boardmateapi.configurations;
|
||||
package be.naaturel.boardmateapi.configurations.configurations;
|
||||
|
||||
import be.naaturel.boardmateapi.common.helpers.Logger;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
@@ -8,7 +9,7 @@ public class Interceptor implements HandlerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
System.out.println("Received : " + request.getRequestURI());
|
||||
Logger.displayInfo("Intercepted : " + request.getRequestURI());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,15 @@
|
||||
package be.naaturel.boardmateapi.configurations;
|
||||
package be.naaturel.boardmateapi.configurations.configurations;
|
||||
|
||||
import be.naaturel.boardmateapi.configurations.properties.AppProperties;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
|
||||
public class MongoConfig {
|
||||
|
||||
private final AppConfigurations conf;
|
||||
private final AppProperties conf;
|
||||
|
||||
public MongoConfig(AppConfigurations appConf) {
|
||||
public MongoConfig(AppProperties appConf) {
|
||||
this.conf = appConf;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package be.naaturel.boardmateapi.configurations.configurations;
|
||||
|
||||
import be.naaturel.boardmateapi.common.helpers.Logger;
|
||||
import be.naaturel.boardmateapi.configurations.properties.MqttProperies;
|
||||
import org.eclipse.paho.client.mqttv3.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MqttConfig {
|
||||
|
||||
private final MqttProperies properties;
|
||||
|
||||
@Autowired
|
||||
public MqttConfig(MqttProperies properties){
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean("mqttPublisher")
|
||||
public MqttClient mqttPublisher() throws MqttException {
|
||||
MqttClient client = new MqttClient(properties.getBrokerUrl(), properties.getClientId());
|
||||
MqttConnectOptions options = new MqttConnectOptions();
|
||||
options.setUserName(properties.getUsername());
|
||||
options.setPassword(properties.getPassword().toCharArray());
|
||||
return client;
|
||||
}
|
||||
|
||||
@Bean("mqttSubscriber")
|
||||
public MqttClient mqttSubscriber() throws MqttException {
|
||||
String subscriberId = properties.getClientId() + "-sub";
|
||||
MqttClient client = new MqttClient(properties.getBrokerUrl(), subscriberId);
|
||||
|
||||
client.setCallback(new MqttCallback() {
|
||||
@Override
|
||||
public void connectionLost(Throwable cause) {
|
||||
Logger.displayError("Connection lost: " + cause.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageArrived(String topic, MqttMessage message) {
|
||||
Logger.displayInfo("Received message on topic " + topic + ": " + message.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliveryComplete(IMqttDeliveryToken token) {
|
||||
// Not needed for subscriber
|
||||
}
|
||||
});
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package be.naaturel.boardmateapi.configurations.configurations;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class SwaggerConfig {
|
||||
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package be.naaturel.boardmateapi.configurations;
|
||||
package be.naaturel.boardmateapi.configurations.properties;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class AppConfigurations {
|
||||
public class AppProperties {
|
||||
|
||||
@Value("${sec.cors.authorizedHots}")
|
||||
public String[] authorizedHosts;
|
||||
@@ -0,0 +1,29 @@
|
||||
package be.naaturel.boardmateapi.configurations.properties;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MqttProperies {
|
||||
|
||||
@Value("${mqtt.broker-url}")
|
||||
private String brokerUrl;
|
||||
|
||||
@Value("${mqtt.client-id}")
|
||||
private String clientId;
|
||||
|
||||
@Value("${mqtt.username}")
|
||||
private String username;
|
||||
|
||||
@Value("${mqtt.password}")
|
||||
private String password;
|
||||
|
||||
@Value("${mqtt.topic}")
|
||||
private String topic;
|
||||
|
||||
public String getBrokerUrl() { return brokerUrl; }
|
||||
public String getClientId() { return clientId; }
|
||||
public String getUsername() { return username; }
|
||||
public String getPassword() { return password; }
|
||||
public String getTopic() { return topic; }
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package be.naaturel.boardmateapi.controllers;
|
||||
|
||||
import be.naaturel.boardmateapi.common.exceptions.ServiceException;
|
||||
import be.naaturel.boardmateapi.services.MqttService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController("/broker")
|
||||
public class BrokerController {
|
||||
|
||||
private final MqttService service;
|
||||
|
||||
@Autowired
|
||||
public BrokerController(MqttService service){
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@PostMapping("/publish/{topic}")
|
||||
public ResponseEntity<ResponseBody<?>> publish(@PathVariable String topic, @RequestBody String message){
|
||||
ResponseBody<?> body = ResponseBody.createEmpty();
|
||||
try {
|
||||
service.subscribe(topic);
|
||||
service.publish(topic, message);
|
||||
body.setSuccess(true);
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(body);
|
||||
} catch (ServiceException se){
|
||||
body.setMessage(se.getMessage());
|
||||
body.setSuccess(false);
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package be.naaturel.boardmateapi.controllers;
|
||||
|
||||
import be.naaturel.boardmateapi.common.exceptions.ServiceException;
|
||||
import be.naaturel.boardmateapi.common.models.Game;
|
||||
import be.naaturel.boardmateapi.controllers.dtos.GameDto;
|
||||
import be.naaturel.boardmateapi.controllers.mappings.GameMapper;
|
||||
@@ -33,53 +34,57 @@ public class GameController {
|
||||
|
||||
@GetMapping("/games/{id}")
|
||||
public ResponseEntity<ResponseBody<GameDto>> retrieveGames(@PathVariable String id){
|
||||
ResponseBody<GameDto> response = ResponseBody.createEmpty();
|
||||
ResponseBody<GameDto> result = ResponseBody.createEmpty();
|
||||
try{
|
||||
Game g = service.retrieveGame(id);
|
||||
GameDto dto = GameMapper.toDto(g);
|
||||
response.setData(dto);
|
||||
response.setSuccess(true);
|
||||
result.setData(dto);
|
||||
result.setSuccess(true);
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.OK)
|
||||
.body(response);
|
||||
.body(result);
|
||||
} catch (Exception e){
|
||||
response.setMessage(e.getMessage());
|
||||
result.setMessage(e.getMessage());
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(response);
|
||||
.body(result);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
public ResponseEntity<ResponseBody<String>> CreateParty(@RequestBody GameDto game){
|
||||
ResponseBody<String> response = ResponseBody.createEmpty();
|
||||
ResponseBody<String> result = ResponseBody.createEmpty();
|
||||
try{
|
||||
Game model = GameMapper.toModel(game);
|
||||
String result = service.create(model);
|
||||
response.setData(result);
|
||||
response.setSuccess(true);
|
||||
String id = service.create(model);
|
||||
result.setData(id);
|
||||
result.setSuccess(true);
|
||||
return ResponseEntity.
|
||||
status(HttpStatus.OK)
|
||||
.body(response);
|
||||
.body(result);
|
||||
} catch (Exception e){
|
||||
response.setMessage(e.getMessage());
|
||||
result.setMessage(e.getMessage());
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(response);
|
||||
.body(result);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/moves/add/{gameId}")
|
||||
public ResponseEntity<?> AddMove(@PathVariable String gameId, @RequestBody String move){
|
||||
public ResponseEntity<ResponseBody<String>> AddMove(@PathVariable String gameId, @RequestBody String move){
|
||||
ResponseBody<String> result = ResponseBody.createEmpty();
|
||||
try{
|
||||
service.addMove(gameId, move);
|
||||
String gamedId = service.addMove(gameId, move);
|
||||
result.setSuccess(true);
|
||||
result.setData(gamedId);
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.OK)
|
||||
.build();
|
||||
} catch (Exception e){
|
||||
.body(result);
|
||||
} catch (ServiceException e){
|
||||
result.setMessage(e.getMessage());
|
||||
return ResponseEntity
|
||||
.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.build();
|
||||
.body(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package be.naaturel.boardmateapi.services;
|
||||
|
||||
import be.naaturel.boardmateapi.common.exceptions.ServiceException;
|
||||
import be.naaturel.boardmateapi.common.models.Game;
|
||||
import be.naaturel.boardmateapi.common.models.Move;
|
||||
import be.naaturel.boardmateapi.repository.GameRepo;
|
||||
@@ -38,9 +39,14 @@ public class GameService {
|
||||
return gameDto.getId();
|
||||
}
|
||||
|
||||
public void addMove(@RequestBody String gameId, @RequestBody String move) {
|
||||
public String addMove(@RequestBody String gameId, @RequestBody String move) throws ServiceException {
|
||||
try {
|
||||
Game g = retrieveGame(gameId);
|
||||
g.addMove(move);
|
||||
save(g);
|
||||
return g.getId();
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package be.naaturel.boardmateapi.services;
|
||||
|
||||
import be.naaturel.boardmateapi.common.exceptions.ServiceException;
|
||||
import be.naaturel.boardmateapi.common.helpers.Logger;
|
||||
import org.eclipse.paho.client.mqttv3.*;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MqttService {
|
||||
|
||||
private final MqttClient publisher;
|
||||
private final MqttClient subscriber;
|
||||
|
||||
public MqttService(
|
||||
@Qualifier("mqttPublisher") MqttClient publisher,
|
||||
@Qualifier("mqttSubscriber") MqttClient subscriber
|
||||
) {
|
||||
this.publisher = publisher;
|
||||
this.subscriber = subscriber;
|
||||
}
|
||||
|
||||
public void publish(String topic, String payload) throws ServiceException {
|
||||
try {
|
||||
MqttMessage message = new MqttMessage(payload.getBytes());
|
||||
message.setQos(1);
|
||||
if(!publisher.isConnected()){
|
||||
publisher.connect();
|
||||
}
|
||||
publisher.publish(topic, message);
|
||||
Logger.displayInfo("Published message: " + payload);
|
||||
} catch (MqttException e) {
|
||||
throw new ServiceException("Unable to publish message", e);
|
||||
} finally {
|
||||
try{
|
||||
if (publisher.isConnected()) publisher.disconnect();
|
||||
} catch (MqttException e){
|
||||
Logger.displayError("Failed to disconnect MQTT client: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void subscribe(String topic) throws ServiceException {
|
||||
try {
|
||||
MqttConnectOptions options = new MqttConnectOptions();
|
||||
options.setAutomaticReconnect(true);
|
||||
options.setCleanSession(true);
|
||||
|
||||
if(!subscriber.isConnected()){
|
||||
subscriber.connect(options);
|
||||
}
|
||||
subscriber.subscribe(topic, 1);
|
||||
Logger.displayInfo("Subscribed to topic: " + topic);
|
||||
|
||||
} catch (MqttException e) {
|
||||
throw new ServiceException("Unable to subscribe", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,10 +10,21 @@ sec.cors.authorizedHots=*
|
||||
sec.cors.authorizedMethods=GET,POST,PUT,DELETE,OPTION
|
||||
sec.cors.authorizedHeader=Authorization,Content-type
|
||||
|
||||
#=============MQTT=============
|
||||
mqtt.broker-url=tcp://test.mosquitto.org:1883
|
||||
mqtt.client-id=board-mate-client
|
||||
|
||||
mqtt.topic=board-mate-test/topic
|
||||
|
||||
|
||||
mqtt.username=yourUsername
|
||||
mqtt.password=yourPassword
|
||||
#=============METRICS=============
|
||||
management.endpoints.web.exposure.include=*
|
||||
management.endpoint.health.show-details=always
|
||||
management.endpoint.prometheus.enabled=true
|
||||
management.endpoints.web.exposure.include=*
|
||||
management.prometheus.metrics.export.enabled=true
|
||||
|
||||
management.metrics.export.otlp.endpoint=http://prometheus:4318/v1/metrics
|
||||
|
||||
#=============DOCUMENTATION=============
|
||||
springdoc.swagger-ui.path=/docs
|
||||
|
||||
Reference in New Issue
Block a user