mirror of
https://github.com/davidalves04/Trabalho-Pratico-SD.git
synced 2025-12-08 20:43:32 +00:00
Compare commits
26 Commits
dash_test
...
snapshot-b
| Author | SHA1 | Date | |
|---|---|---|---|
| 043ba7d185 | |||
| 25f2876c34 | |||
| 7cbecc4fab | |||
| 72db59415f | |||
| 60b4f0c2b6 | |||
| 81f842e2bb | |||
|
|
108d2e544c | ||
| 23f7a74798 | |||
| d7dec0d73e | |||
|
|
534a880e3e | ||
|
|
ba3233eae1 | ||
|
|
d20040835c | ||
|
|
2399b4b472 | ||
|
|
974debf7db | ||
| 8e95bc4c01 | |||
| 33ed84b0c2 | |||
| 9093b13c5d | |||
| 12b7aabe87 | |||
| c30aa25de0 | |||
| 3689f7a207 | |||
| bb18c1119e | |||
| f0dbdb551d | |||
| f519c9aba7 | |||
| 1216089e80 | |||
| 211ea25ca5 | |||
|
|
af9b091e76 |
99
.github/workflows/maven.yml
vendored
Normal file
99
.github/workflows/maven.yml
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
name: Java CI with Maven
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ "dev", "cleanup" ]
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
cache: maven
|
||||
- name: Build with Maven
|
||||
run: mvn -B package
|
||||
working-directory: main
|
||||
- name: Upload built JAR
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: package
|
||||
path: main/target/*.jar
|
||||
- name: Generate dependency graph
|
||||
run: mvn -B -f main/pom.xml com.github.ferstl:depgraph-maven-plugin:4.0.1:graph
|
||||
- name: Upload dependency graph artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dependency-graph
|
||||
path: main/target/**
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
cache: maven
|
||||
- name: Build with Maven (Skip Tests)
|
||||
run: mvn -B package -DskipTests
|
||||
working-directory: main
|
||||
- name: Create JPackage App Image
|
||||
shell: pwsh
|
||||
run: |
|
||||
New-Item -ItemType Directory -Force -Path "dist"
|
||||
jpackage --name "DTSS" `
|
||||
--input main/target `
|
||||
--main-jar main-1.0-SNAPSHOT.jar `
|
||||
--dest dist `
|
||||
--type app-image `
|
||||
--win-console
|
||||
- name: Inject java.exe
|
||||
shell: pwsh
|
||||
run: |
|
||||
$javaPath = (Get-Command java).Source
|
||||
Copy-Item -Path $javaPath -Destination "dist/DTSS/runtime/bin/"
|
||||
- name: Zip Windows Release
|
||||
shell: pwsh
|
||||
run: |
|
||||
Compress-Archive -Path "dist/DTSS" -DestinationPath "dist/DTSS-Windows.zip"
|
||||
- name: Upload Windows Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-package
|
||||
path: dist/DTSS-Windows.zip
|
||||
|
||||
publish-release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build, build-windows]
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Download Linux JAR
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: package
|
||||
path: main/target/
|
||||
- name: Download Windows Zip
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-package
|
||||
path: windows-dist/
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
main/target/*.jar
|
||||
windows-dist/*.zip
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -47,4 +47,3 @@ build/
|
||||
|
||||
# Other
|
||||
*.swp
|
||||
*.pdf
|
||||
BIN
Enunciado.pdf
Normal file
BIN
Enunciado.pdf
Normal file
Binary file not shown.
620
README.md
Normal file
620
README.md
Normal file
@@ -0,0 +1,620 @@
|
||||
# Sistema de Simulação de Tráfego Distribuído
|
||||
|
||||
Sistema distribuído de simulação de tráfego.
|
||||
---
|
||||
|
||||
## Índice
|
||||
|
||||
- [Visão Geral](#visão-geral)
|
||||
- [Arquitetura](#arquitetura)
|
||||
- [Protocolo de Comunicação](#protocolo-de-comunicação)
|
||||
- [Estrutura do Projeto](#estrutura-do-projeto)
|
||||
- [Instalação e Execução](#instalação-e-execução)
|
||||
- [Documentação](#documentação)
|
||||
- [Desenvolvimento](#desenvolvimento)
|
||||
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Este projeto implementa uma simulação distribuída de tráfego veicular numa rede de cruzamentos. O sistema utiliza:
|
||||
|
||||
- **Processos independentes** para cada cruzamento
|
||||
- **Threads** para controlar os semáforos dentro de cada cruzamento
|
||||
- **Comunicação via sockets** para transferência de veículos entre cruzamentos
|
||||
- **Simulação de eventos discretos** (DES) para gerir o tempo de simulação
|
||||
|
||||
### Características Principais
|
||||
|
||||
- Simulação determinística e reproduzível
|
||||
- Comunicação assíncrona entre processos
|
||||
- Protocolo de mensagens baseado em JSON
|
||||
- Dashboard em tempo real (planeado)
|
||||
- Estatísticas detalhadas de desempenho
|
||||
|
||||
---
|
||||
|
||||
## Arquitetura
|
||||
|
||||
### Visão Geral do Sistema
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ SISTEMA DISTRIBUÍDO │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Coordenador │ ────────────────────────>│ Dashboard │ │
|
||||
│ │ / Gerador │ │ │
|
||||
│ └──────┬───────┘ └──────▲───────┘ │
|
||||
│ │ │ │
|
||||
│ │ Gera veículos Stats │ │
|
||||
│ │ │ │
|
||||
│ ▼ │ │
|
||||
│ ┌─────────────────────────────────────────────────┴──────┐ │
|
||||
│ │ Rede de Cruzamentos (Processos) │ │
|
||||
│ │ │ │
|
||||
│ │ ┌────┐ ┌────┐ ┌────┐ │ │
|
||||
│ │ │Cr1 │◄───────►│Cr2 │◄───────►│Cr3 │ │ │
|
||||
│ │ └─┬──┘ └─┬──┘ └─┬──┘ │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ │ │ ┌────▼────┐ │ │ │
|
||||
│ │ └────────►│ Cr4 │◄────────┘ │ │
|
||||
│ │ └────┬────┘ │ │
|
||||
│ │ │ │ │
|
||||
│ │ ┌────▼────┐ │ │
|
||||
│ │ │ Cr5 │ │ │
|
||||
│ │ └────┬────┘ │ │
|
||||
│ └───────────────────┼─────────────────────────────────────┤ │
|
||||
│ │ │ │
|
||||
│ ▼ │ │
|
||||
│ ┌──────────────┐ │ │
|
||||
│ │ Nó de Saída │ │ │
|
||||
│ │ (S) │ │ │
|
||||
│ └──────────────┘ │ │
|
||||
│ │ │
|
||||
└────────────────────────────────────────────────────────────┘ │
|
||||
```
|
||||
|
||||
### Componentes
|
||||
|
||||
1. **Coordenador/Gerador**: Gera veículos e injeta no sistema
|
||||
2. **Cruzamentos (Cr1-Cr5)**: Processos independentes que gerem tráfego local
|
||||
3. **Nó de Saída (S)**: Recolhe estatísticas de veículos que saem do sistema
|
||||
4. **Dashboard Server**: Agrega e exibe dados em tempo real
|
||||
|
||||
---
|
||||
|
||||
## Protocolo de Comunicação
|
||||
|
||||
### Formato de Serialização: JSON (Gson)
|
||||
|
||||
O sistema utiliza JSON como formato de serialização por ser mais rápido, seguro e legível que a serialização em Java.
|
||||
|
||||
### Estrutura de Mensagens
|
||||
|
||||
Todas as mensagens seguem o formato base:
|
||||
|
||||
```json
|
||||
{
|
||||
"messageId": "uuid",
|
||||
"type": "MESSAGE_TYPE",
|
||||
"senderId": "sender_id",
|
||||
"destinationId": "destination_id",
|
||||
"timestamp": 1729595234567,
|
||||
"payload": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### Tipos de Mensagens
|
||||
|
||||
#### 1. VEHICLE_TRANSFER
|
||||
|
||||
Transfere um veículo entre cruzamentos.
|
||||
|
||||
**Estrutura:**
|
||||
```json
|
||||
{
|
||||
"messageId": "a3c5e7f9-1234-5678-90ab-cdef12345678",
|
||||
"type": "VEHICLE_TRANSFER",
|
||||
"senderId": "Cr1",
|
||||
"destinationId": "Cr2",
|
||||
"timestamp": 1729595234567,
|
||||
"payload": {
|
||||
"id": "V123",
|
||||
"type": "LIGHT",
|
||||
"entryTime": 15.7,
|
||||
"route": ["Cr1", "Cr2", "Cr5", "S"],
|
||||
"currentRouteIndex": 1,
|
||||
"totalWaitingTime": 3.2,
|
||||
"totalCrossingTime": 1.8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Fluxo:**
|
||||
1. Veículo completa travessia no Cr1
|
||||
2. Cr1 serializa mensagem VEHICLE_TRANSFER
|
||||
3. Envia para Cr2 via socket
|
||||
4. Cr2 desserializa e adiciona veículo à fila
|
||||
|
||||
#### 2. STATS_UPDATE
|
||||
|
||||
Envia estatísticas de um cruzamento para o Dashboard.
|
||||
|
||||
**Estrutura:**
|
||||
```json
|
||||
{
|
||||
"messageId": "b4d6e8f0-2345-6789-01bc-def123456789",
|
||||
"type": "STATS_UPDATE",
|
||||
"senderId": "Cr3",
|
||||
"destinationId": "Dashboard",
|
||||
"timestamp": 1729595234789,
|
||||
"payload": {
|
||||
"intersectionId": "Cr3",
|
||||
"queueLengths": {
|
||||
"North": 5,
|
||||
"South": 3,
|
||||
"East": 7,
|
||||
"West": 2
|
||||
},
|
||||
"vehiclesProcessed": 142,
|
||||
"averageWaitTime": 4.5,
|
||||
"currentTime": 123.45
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Frequência:** A cada 10 segundos (configurável)
|
||||
|
||||
#### 3. VEHICLE_EXIT
|
||||
|
||||
Notifica quando um veículo sai do sistema.
|
||||
|
||||
**Estrutura:**
|
||||
```json
|
||||
{
|
||||
"messageId": "c5e7f9a1-3456-7890-12bc-def123456789",
|
||||
"type": "VEHICLE_EXIT",
|
||||
"senderId": "Cr5",
|
||||
"destinationId": "ExitNode",
|
||||
"timestamp": 1729595234890,
|
||||
"payload": {
|
||||
"id": "V123",
|
||||
"type": "LIGHT",
|
||||
"entryTime": 15.7,
|
||||
"exitTime": 45.2,
|
||||
"totalSystemTime": 29.5,
|
||||
"totalWaitingTime": 8.3,
|
||||
"totalCrossingTime": 4.8,
|
||||
"routeTaken": ["Cr1", "Cr2", "Cr5", "S"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. HEARTBEAT
|
||||
|
||||
Mantém a ligação ativa e monitoriza a saúde dos processos.
|
||||
|
||||
**Estrutura:**
|
||||
```json
|
||||
{
|
||||
"messageId": "d6e8f0a2-4567-8901-23cd-ef1234567890",
|
||||
"type": "HEARTBEAT",
|
||||
"senderId": "Cr1",
|
||||
"destinationId": "Coordinator",
|
||||
"timestamp": 1729595235000,
|
||||
"payload": {
|
||||
"status": "RUNNING",
|
||||
"uptime": 120.5,
|
||||
"vehiclesInQueue": 12
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Frequência:** A cada 5 segundos
|
||||
|
||||
#### 5. LIGHT_CHANGE
|
||||
|
||||
Notifica mudança de estado de semáforo (para logging/debugging).
|
||||
|
||||
**Estrutura:**
|
||||
```json
|
||||
{
|
||||
"messageId": "e7f9a1b3-5678-9012-34de-f12345678901",
|
||||
"type": "LIGHT_CHANGE",
|
||||
"senderId": "Cr1-North",
|
||||
"destinationId": "Dashboard",
|
||||
"timestamp": 1729595235100,
|
||||
"payload": {
|
||||
"lightId": "Cr1-North",
|
||||
"previousState": "RED",
|
||||
"newState": "GREEN",
|
||||
"queueSize": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Tipos de Veículos
|
||||
|
||||
```json
|
||||
{
|
||||
"BIKE": {
|
||||
"probability": 0.20,
|
||||
"crossingTime": 1.5
|
||||
},
|
||||
"LIGHT": {
|
||||
"probability": 0.60,
|
||||
"crossingTime": 2.0
|
||||
},
|
||||
"HEAVY": {
|
||||
"probability": 0.20,
|
||||
"crossingTime": 4.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Estados dos Semáforos
|
||||
|
||||
```
|
||||
RED → Veículos aguardam na fila
|
||||
GREEN → Veículos podem atravessar
|
||||
```
|
||||
|
||||
### Exemplo de Comunicação Completa
|
||||
|
||||
```
|
||||
Tempo Processo Ação Mensagem
|
||||
------ --------- ------------------------------------- ------------------
|
||||
15.7s Gerador Gera veículo V123 -
|
||||
15.7s Gerador → Injeta V123 em Cr1 VEHICLE_TRANSFER
|
||||
18.2s Cr1 V123 inicia travessia -
|
||||
20.2s Cr1 V123 completa travessia -
|
||||
20.2s Cr1 → Cr2 Transfere V123 para Cr2 VEHICLE_TRANSFER
|
||||
23.5s Cr2 V123 inicia travessia -
|
||||
25.5s Cr2 V123 completa travessia -
|
||||
25.5s Cr2 → Cr5 Transfere V123 para Cr5 VEHICLE_TRANSFER
|
||||
28.0s Cr5 V123 inicia travessia -
|
||||
30.0s Cr5 V123 completa travessia -
|
||||
30.0s Cr5 → Exit V123 sai do sistema VEHICLE_EXIT
|
||||
30.0s Exit → Dash Estatísticas de V123 STATS_UPDATE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Estrutura do Projeto
|
||||
|
||||
```
|
||||
Trabalho-Pratico-SD/
|
||||
├── README.md # Este ficheiro
|
||||
├── TODO.md # Plano de desenvolvimento
|
||||
├── main/
|
||||
│ ├── pom.xml # Configuração do Maven
|
||||
│ ├── docs/
|
||||
│ │ ├── README.md # Índice da documentação
|
||||
│ │ ├── SERIALIZATION_SPECIFICATION.md
|
||||
│ │ ├── SERIALIZATION_DECISION.md
|
||||
│ │ ├── SERIALIZATION_SUMMARY.md
|
||||
│ │ └── SERIALIZATION_ARCHITECTURE.md
|
||||
│ ├── src/
|
||||
│ │ ├── main/java/sd/
|
||||
│ │ │ ├── Entry.java # Ponto de entrada
|
||||
│ │ │ ├── config/
|
||||
│ │ │ │ └── SimulationConfig.java
|
||||
│ │ │ ├── engine/
|
||||
│ │ │ │ └── SimulationEngine.java
|
||||
│ │ │ ├── model/
|
||||
│ │ │ │ ├── Event.java
|
||||
│ │ │ │ ├── EventType.java
|
||||
│ │ │ │ ├── Intersection.java
|
||||
│ │ │ │ ├── Message.java # Estrutura de mensagens
|
||||
│ │ │ │ ├── MessageType.java # Tipos de mensagens
|
||||
│ │ │ │ ├── TrafficLight.java
|
||||
│ │ │ │ ├── Vehicle.java
|
||||
│ │ │ │ └── VehicleType.java
|
||||
│ │ │ ├── serialization/ # Sistema de serialização
|
||||
│ │ │ │ ├── MessageSerializer.java
|
||||
│ │ │ │ ├── SerializationException.java
|
||||
│ │ │ │ ├── JsonMessageSerializer.java
|
||||
│ │ │ │ ├── SerializerFactory.java
|
||||
│ │ │ │ ├── SerializationExample.java
|
||||
│ │ │ │ └── README.md
|
||||
│ │ │ └── util/
|
||||
│ │ │ ├── RandomGenerator.java
|
||||
│ │ │ ├── StatisticsCollector.java
|
||||
│ │ │ └── VehicleGenerator.java
|
||||
│ │ └── test/java/
|
||||
│ │ ├── SimulationTest.java
|
||||
│ │ └── sd/serialization/
|
||||
│ │ └── SerializationTest.java
|
||||
│ └── target/ # Ficheiros compilados
|
||||
└── .vscode/ # Configuração do VS Code
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Instalação e Execução
|
||||
|
||||
### Pré-requisitos
|
||||
|
||||
- **Java 17** ou superior
|
||||
- **Maven 3.8+**
|
||||
- **Git**
|
||||
|
||||
### Instalação
|
||||
|
||||
```bash
|
||||
# Clonar o repositório
|
||||
git clone https://github.com/davidalves04/Trabalho-Pratico-SD.git
|
||||
cd Trabalho-Pratico-SD/main
|
||||
|
||||
# Compilar o projeto
|
||||
mvn clean compile
|
||||
|
||||
# Executar os testes
|
||||
mvn test
|
||||
```
|
||||
|
||||
### Execução
|
||||
|
||||
#### Simulação Básica (Single Process)
|
||||
|
||||
```bash
|
||||
mvn exec:java -Dexec.mainClass="sd.Entry"
|
||||
```
|
||||
|
||||
#### Exemplo de Serialização
|
||||
|
||||
```bash
|
||||
mvn exec:java -Dexec.mainClass="sd.serialization.SerializationExample"
|
||||
```
|
||||
|
||||
#### Configuração
|
||||
|
||||
Editar `src/main/resources/simulation.properties`:
|
||||
|
||||
```properties
|
||||
# Duração da simulação (segundos)
|
||||
simulation.duration=60.0
|
||||
|
||||
# Modelo de chegada: FIXED ou POISSON
|
||||
arrival.model=POISSON
|
||||
|
||||
# Taxa de chegada (veículos/segundo)
|
||||
arrival.rate=0.5
|
||||
|
||||
# Intervalo de atualização de estatísticas (segundos)
|
||||
stats.update.interval=10.0
|
||||
|
||||
# Distribuição de tipos de veículos
|
||||
vehicle.type.bike.probability=0.20
|
||||
vehicle.type.light.probability=0.60
|
||||
vehicle.type.heavy.probability=0.20
|
||||
|
||||
# Tempos de travessia por tipo (segundos)
|
||||
vehicle.type.bike.crossing.time=1.5
|
||||
vehicle.type.light.crossing.time=2.0
|
||||
vehicle.type.heavy.crossing.time=4.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Documentação
|
||||
|
||||
### Documentação de Serialização
|
||||
|
||||
A documentação completa sobre o protocolo de serialização está disponível em:
|
||||
|
||||
- **[Índice Completo](./main/docs/README.md)** - Navegação da documentação
|
||||
- **[Especificação](./main/docs/SERIALIZATION_SPECIFICATION.md)** - Design detalhado
|
||||
- **[Guia de Decisão](./main/docs/SERIALIZATION_DECISION.md)** - Porquê JSON?
|
||||
- **[Resumo](./main/docs/SERIALIZATION_SUMMARY.md)** - Estado de implementação
|
||||
- **[Arquitetura](./main/docs/SERIALIZATION_ARCHITECTURE.md)** - Diagramas visuais
|
||||
|
||||
### Guias de Utilização
|
||||
|
||||
- **[Serialization README](./main/src/main/java/sd/serialization/README.md)** - Como utilizar os serializers
|
||||
|
||||
### Exemplos de Código
|
||||
|
||||
```java
|
||||
// Criar serializer
|
||||
MessageSerializer serializer = SerializerFactory.createDefault();
|
||||
|
||||
// Serializar mensagem
|
||||
Vehicle vehicle = new Vehicle("V123", VehicleType.LIGHT, 10.5, route);
|
||||
Message message = new Message(
|
||||
MessageType.VEHICLE_TRANSFER,
|
||||
"Cr1",
|
||||
"Cr2",
|
||||
vehicle
|
||||
);
|
||||
byte[] data = serializer.serialize(message);
|
||||
|
||||
// Enviar via socket
|
||||
outputStream.write(data);
|
||||
|
||||
// Receber e desserializar
|
||||
byte[] received = inputStream.readAllBytes();
|
||||
Message msg = serializer.deserialize(received, Message.class);
|
||||
Vehicle v = msg.getPayloadAs(Vehicle.class);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Desenvolvimento
|
||||
|
||||
### Estado do Projeto
|
||||
|
||||
| Componente | Estado | Notas |
|
||||
|------------|--------|-------|
|
||||
| Modelo de Dados | Completo | Vehicle, Message, Event, etc. |
|
||||
| Simulação DES | Completo | Single-process funcional |
|
||||
| Serialização | Completo | JSON e Java implementados |
|
||||
| Testes | 14/14 | Suite de serialização |
|
||||
| Processos Distribuídos | Planeado | Próxima etapa |
|
||||
| Comunicação Sockets | Planeado | Em design |
|
||||
| Dashboard | Planeado | UI web |
|
||||
|
||||
### Roteiro de Desenvolvimento
|
||||
|
||||
#### Fase 1: Fundações (Concluído)
|
||||
- Modelação de classes
|
||||
- Simulação DES single-process
|
||||
- Design de protocolo de serialização
|
||||
- Implementação JSON/Java serialization
|
||||
- Testes unitários
|
||||
|
||||
#### Fase 2: Distribuição (Em Curso)
|
||||
- Implementar comunicação via sockets
|
||||
- Separar cruzamentos em processos
|
||||
- Implementar threads de semáforos
|
||||
- Testar comunicação entre processos
|
||||
|
||||
#### Fase 3: Dashboard e Monitorização
|
||||
- Dashboard server
|
||||
- UI web em tempo real
|
||||
- Visualização de estatísticas
|
||||
- Logs estruturados
|
||||
|
||||
#### Fase 4: Optimização e Análise
|
||||
- Testes de carga
|
||||
- Análise de diferentes políticas
|
||||
- Recolha de métricas
|
||||
- Relatório final
|
||||
|
||||
### Executar Testes
|
||||
|
||||
```bash
|
||||
# Todos os testes
|
||||
mvn test
|
||||
|
||||
# Apenas testes de serialização
|
||||
mvn test -Dtest=SerializationTest
|
||||
|
||||
# Com relatório de cobertura
|
||||
mvn test jacoco:report
|
||||
```
|
||||
|
||||
### Contribuir
|
||||
|
||||
1. Fork o projeto
|
||||
2. Criar uma branch para a funcionalidade (`git checkout -b feature/MinhaFuncionalidade`)
|
||||
3. Commit das alterações (`git commit -m 'Adiciona MinhaFuncionalidade'`)
|
||||
4. Push para a branch (`git push origin feature/MinhaFuncionalidade`)
|
||||
5. Abrir um Pull Request
|
||||
|
||||
---
|
||||
|
||||
## Métricas de Desempenho
|
||||
|
||||
### Serialização
|
||||
|
||||
| Formato | Tamanho | Latência | Throughput |
|
||||
|---------|---------|----------|------------|
|
||||
| JSON | 300 bytes | 40.79 μs | ~24k msgs/s |
|
||||
| Java | 657 bytes | 33.34 μs | ~30k msgs/s |
|
||||
|
||||
**Conclusão**: JSON é 54% menor com overhead desprezível (7 μs)
|
||||
|
||||
### Simulação
|
||||
|
||||
- **Veículos gerados/s**: ~0.5-1.0 (configurável)
|
||||
- **Throughput**: ~0.2 veículos/s (saída)
|
||||
- **Tempo de execução**: 140ms para 60s de simulação
|
||||
- **Overhead**: < 0.25% do tempo simulado
|
||||
|
||||
---
|
||||
|
||||
## Protocolo de Mensagens - Resumo
|
||||
|
||||
### Formato Base
|
||||
|
||||
```
|
||||
+------------------+
|
||||
| Message Header |
|
||||
|------------------|
|
||||
| messageId | UUID único
|
||||
| type | Enum MessageType
|
||||
| senderId | ID do processo remetente
|
||||
| destinationId | ID do processo destino (null = broadcast)
|
||||
| timestamp | Tempo de criação (ms)
|
||||
+------------------+
|
||||
| Payload |
|
||||
|------------------|
|
||||
| Object | Dados específicos do tipo de mensagem
|
||||
+------------------+
|
||||
```
|
||||
|
||||
### Serialização
|
||||
|
||||
- **Formato**: JSON (UTF-8)
|
||||
- **Biblioteca**: Gson 2.10.1
|
||||
- **Codificação**: UTF-8
|
||||
- **Compressão**: Opcional (gzip)
|
||||
|
||||
### Transporte
|
||||
|
||||
- **Protocolo**: TCP/IP
|
||||
- **Porta base**: 5000+ (configurável)
|
||||
- **Timeout**: 30s
|
||||
- **Keep-alive**: Heartbeat a cada 5s
|
||||
|
||||
---
|
||||
|
||||
## Segurança
|
||||
|
||||
### Considerações
|
||||
|
||||
1. **Validação de Mensagens**
|
||||
- Verificar tipos esperados
|
||||
- Validar intervalos de valores
|
||||
- Rejeitar mensagens malformadas
|
||||
|
||||
2. **Autenticação** (Planeado)
|
||||
- Autenticação baseada em token
|
||||
- Whitelist de processos
|
||||
|
||||
3. **Encriptação** (Opcional)
|
||||
- TLS/SSL para produção
|
||||
- Não necessário para ambiente de desenvolvimento local
|
||||
|
||||
---
|
||||
|
||||
## Licença
|
||||
|
||||
Este projeto é desenvolvido para fins académicos no âmbito da disciplina de Sistemas Distribuídos (SD) do Instituto Politécnico do Porto.
|
||||
|
||||
---
|
||||
|
||||
## Equipa
|
||||
|
||||
**Instituição**: Instituto Politécnico do Porto
|
||||
**Curso**: Sistemas Distribuídos
|
||||
**Ano Letivo**: 2025-2026 (1º Semestre)
|
||||
|
||||
---
|
||||
|
||||
## Suporte
|
||||
|
||||
Para questões ou problemas:
|
||||
|
||||
1. Consultar a [documentação](./main/docs/README.md)
|
||||
2. Ver [exemplos de código](./main/src/main/java/sd/serialization/SerializationExample.java)
|
||||
3. Executar testes: `mvn test`
|
||||
4. Abrir issue no GitHub
|
||||
|
||||
---
|
||||
|
||||
## Ligações Úteis
|
||||
|
||||
- [Documentação do Projeto](./main/docs/README.md)
|
||||
- [Plano de Desenvolvimento](./TODO.md)
|
||||
- [Especificação de Serialização](./main/docs/SERIALIZATION_SPECIFICATION.md)
|
||||
- [Guia de Serialização](./main/src/main/java/sd/serialization/README.md)
|
||||
|
||||
---
|
||||
|
||||
**Última actualização**: 23 de outubro de 2025
|
||||
**Versão**: 1.0.0
|
||||
**Estado**: Em Desenvolvimento Activo
|
||||
134
STEP2_SUMMARY.md
Normal file
134
STEP2_SUMMARY.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# 🏁 Single-Process Prototype — Implementation Summary
|
||||
|
||||
**Status:** ✅ Complete
|
||||
**Date:** October 22, 2025
|
||||
**Branch:** `8-single-process-prototype`
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The single-process prototype implements a **discrete event simulation (DES)** of a 3×3 urban grid with five intersections, realistic vehicle behavior, and fully synchronized traffic lights. Everything runs under one process, laying the groundwork for the distributed architecture in Phase 3.
|
||||
|
||||
---
|
||||
|
||||
## Core Architecture
|
||||
|
||||
### **SimulationEngine**
|
||||
|
||||
Drives the DES loop with a priority queue of timestamped events — vehicles, lights, crossings, and periodic stats updates. Handles five intersections (Cr1–Cr5) and six event types.
|
||||
|
||||
**Main loop:**
|
||||
|
||||
```
|
||||
while (events && time < duration):
|
||||
event = nextEvent()
|
||||
time = event.timestamp
|
||||
handle(event)
|
||||
```
|
||||
|
||||
### **VehicleGenerator**
|
||||
|
||||
Spawns vehicles via:
|
||||
|
||||
* **Poisson arrivals** (λ = 0.5 veh/s) or fixed intervals
|
||||
* **Probabilistic routes** from E1–E3
|
||||
* **Type distribution**: 20% BIKE, 60% LIGHT, 20% HEAVY
|
||||
|
||||
### **StatisticsCollector**
|
||||
|
||||
Tracks system-wide and per-type metrics: throughput, avg. wait, queue sizes, light cycles — updated every 10 s and at simulation end.
|
||||
|
||||
---
|
||||
|
||||
## Model Highlights
|
||||
|
||||
* **Vehicle** – type, route, timings, lifecycle.
|
||||
* **Intersection** – routing tables, traffic lights, queues.
|
||||
* **TrafficLight** – red/green cycles with FIFO queues.
|
||||
* **Event** – timestamped, comparable; 6 types for all DES actions.
|
||||
|
||||
---
|
||||
|
||||
## Configuration (`simulation.properties`)
|
||||
|
||||
```properties
|
||||
simulation.duration=60.0
|
||||
simulation.arrival.model=POISSON
|
||||
simulation.arrival.rate=0.5
|
||||
|
||||
vehicle.bike.crossingTime=1.5
|
||||
vehicle.light.crossingTime=2.0
|
||||
vehicle.heavy.crossingTime=4.0
|
||||
|
||||
statistics.update.interval=10.0
|
||||
```
|
||||
|
||||
**Speed logic:**
|
||||
`t_bike = 0.5×t_car`, `t_heavy = 2×t_car`.
|
||||
|
||||
---
|
||||
|
||||
## Topology
|
||||
|
||||
```
|
||||
E1→Cr1→Cr4→Cr5→S
|
||||
E2→Cr2→Cr5→S
|
||||
E3→Cr3→S
|
||||
Bi-dir: Cr1↔Cr2, Cr2↔Cr3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Results
|
||||
|
||||
**Unit Tests:** 7/7 ✅
|
||||
**60-Second Simulation:**
|
||||
|
||||
* Generated: 22 vehicles
|
||||
* Completed: 5 (22.7%)
|
||||
* Avg system time: 15.47 s
|
||||
* Throughput: 0.08 veh/s
|
||||
* All lights & intersections operational
|
||||
|
||||
**Performance:**
|
||||
~0.03 s real-time run (≈2000× speed-up), < 50 MB RAM.
|
||||
|
||||
---
|
||||
|
||||
## Code Structure
|
||||
|
||||
```
|
||||
sd/
|
||||
├── engine/SimulationEngine.java
|
||||
├── model/{Vehicle,Intersection,TrafficLight,Event}.java
|
||||
├── util/{VehicleGenerator,StatisticsCollector}.java
|
||||
└── config/SimulationConfig.java
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Flow
|
||||
|
||||
1. Initialize intersections, lights, first events.
|
||||
2. Process events chronologically.
|
||||
3. Vehicles follow routes → queue → cross → exit.
|
||||
4. Lights toggle, queues drain, stats update.
|
||||
5. Print summary and performance metrics.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps — Phase 3
|
||||
|
||||
* Split intersections into independent **processes**.
|
||||
* Add **socket-based communication**.
|
||||
* Run **traffic lights as threads**.
|
||||
* Enable **distributed synchronization** and fault handling.
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
Solid single-process DES ✅
|
||||
Everything’s working — traffic lights, routing, vehicles, stats.
|
||||
Ready to go distributed next.
|
||||
27
TODO.md
27
TODO.md
@@ -1,3 +1,26 @@
|
||||
## ✅ SINGLE-PROCESS PROTOTYPE - COMPLETED
|
||||
|
||||
### Phase 2 Status: DONE ✅
|
||||
|
||||
All components for the single-process prototype have been successfully implemented and tested:
|
||||
|
||||
- ✅ **SimulationEngine** - Priority queue-based discrete event simulation
|
||||
- ✅ **VehicleGenerator** - Poisson and Fixed arrival models
|
||||
- ✅ **StatisticsCollector** - Comprehensive metrics tracking
|
||||
- ✅ **Entry point** - Main simulation runner
|
||||
- ✅ **60s test simulation** - Successfully validated event processing and routing
|
||||
|
||||
### Test Results:
|
||||
- All 7 unit tests passing
|
||||
- 60-second simulation completed successfully
|
||||
- Generated 22 vehicles with 5 completing their routes
|
||||
- Traffic light state changes working correctly
|
||||
- Vehicle routing through intersections validated
|
||||
|
||||
---
|
||||
|
||||
## NEXT: Distributed Architecture Implementation
|
||||
|
||||
### Compreender os Conceitos Fundamentais
|
||||
|
||||
Primeiro, as tecnologias e paradigmas chave necessários para este projeto devem ser totalmente compreendidos.
|
||||
@@ -16,7 +39,7 @@ Primeiro, as tecnologias e paradigmas chave necessários para este projeto devem
|
||||
|
||||
- Uma **lista de eventos** central, frequentemente uma fila de prioridades, será necessária para armazenar eventos futuros, ordenados pelo seu timestamp. O ciclo principal da simulação retira o próximo evento da lista, processa-o e adiciona quaisquer novos eventos que resultem dele.
|
||||
|
||||
- **Processo de Poisson:** Para o modelo "mais realista" de chegadas de veículos, é especificado um processo de Poisson. A principal conclusão é que o tempo _entre_ chegadas consecutivas de veículos segue uma **distribuição exponencial**. Em Java, este intervalo pode ser gerado usando `Math.log(1 - Math.random()) / -lambda`, onde `lambda` (λi) é a taxa de chegada especificada.
|
||||
- **Processo de Poisson:** Para o modelo 'mais realista' de chegadas de veículos, é especificado um processo de Poisson. A principal conclusão é que o tempo _entre_ chegadas consecutivas de veículos segue uma **distribuição exponencial**. Em Java, este intervalo pode ser gerado usando `Math.log(1 - Math.random()) / -lambda`, onde `lambda` (λi) é a taxa de chegada especificada.
|
||||
|
||||
|
||||
---
|
||||
@@ -172,4 +195,4 @@ Assim que o sistema completo estiver a funcionar, as experiências exigidas pela
|
||||
|
||||
- **Debugging:** Debugging de sistemas distribuídos podem ser difíceis. Uma framework de logging (como Log4j 2 ou SLF4J) pode ser usada para registar eventos//alterações de estado nos diferentes processos.
|
||||
|
||||
- **Configuração:** Valores como endereços IP, números de porta ou parâmetros da simulação não devem ser "hardcoded". Um ficheiro de configuração (ex: um ficheiro `.properties` ou `.json`) torna a aplicação mais fácil de executar e testar.
|
||||
- **Configuração:** Valores como endereços IP, números de porta ou parâmetros da simulação não devem ser "hardcoded". Um ficheiro de configuração (ex: um ficheiro `.properties` ou `.json`) torna a aplicação mais fácil de executar e testar.
|
||||
|
||||
41
main/pom.xml
41
main/pom.xml
@@ -22,6 +22,47 @@
|
||||
<version>5.10.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Gson for JSON serialization -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Maven Exec Plugin for running examples -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<mainClass>sd.Entry</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>sd.Entry</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -31,7 +31,7 @@ public class SimulationConfig {
|
||||
* (por exemplo quando executado a partir do classpath/jar),
|
||||
* faz fallback para carregar a partir do classpath usando o ClassLoader.
|
||||
*/
|
||||
IOException lastException = null;
|
||||
IOException lastException = null; //FIXME: melhorar esta parte para reportar erros de forma mais clara
|
||||
|
||||
try {
|
||||
try (InputStream input = new FileInputStream(filePath)) {
|
||||
|
||||
@@ -264,32 +264,19 @@ public class SimulationEngine {
|
||||
*/
|
||||
private void processEvent(Event event) {
|
||||
switch (event.getType()) {
|
||||
case VEHICLE_GENERATION:
|
||||
handleVehicleGeneration();
|
||||
break;
|
||||
case VEHICLE_GENERATION -> handleVehicleGeneration();
|
||||
|
||||
case VEHICLE_ARRIVAL:
|
||||
handleVehicleArrival(event);
|
||||
break;
|
||||
case VEHICLE_ARRIVAL -> handleVehicleArrival(event);
|
||||
|
||||
case TRAFFIC_LIGHT_CHANGE:
|
||||
handleTrafficLightChange(event);
|
||||
break;
|
||||
case TRAFFIC_LIGHT_CHANGE -> handleTrafficLightChange(event);
|
||||
|
||||
case CROSSING_START:
|
||||
handleCrossingStart(event);
|
||||
break;
|
||||
case CROSSING_START -> handleCrossingStart(event);
|
||||
|
||||
case CROSSING_END:
|
||||
handleCrossingEnd(event);
|
||||
break;
|
||||
case CROSSING_END -> handleCrossingEnd(event);
|
||||
|
||||
case STATISTICS_UPDATE:
|
||||
handleStatisticsUpdate();
|
||||
break;
|
||||
case STATISTICS_UPDATE -> handleStatisticsUpdate();
|
||||
|
||||
default:
|
||||
System.err.println("Unknown event type: " + event.getType());
|
||||
default -> System.err.println("Unknown event type: " + event.getType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +373,7 @@ public class SimulationEngine {
|
||||
* @param vehicle The vehicle to process.
|
||||
* @param intersection The intersection where the vehicle is.
|
||||
*/
|
||||
private void tryProcessVehicle(Vehicle vehicle, Intersection intersection) {
|
||||
private void tryProcessVehicle(Vehicle vehicle, Intersection intersection) { //FIXME
|
||||
// Find the direction (and light) this vehicle is queued at
|
||||
// This logic is a bit flawed: it just finds the *first* non-empty queue
|
||||
// A better approach would be to get the light from the vehicle's route
|
||||
@@ -591,16 +578,12 @@ public class SimulationEngine {
|
||||
* @return The crossing time in seconds.
|
||||
*/
|
||||
private double getCrossingTime(VehicleType type) {
|
||||
switch (type) {
|
||||
case BIKE:
|
||||
return config.getBikeVehicleCrossingTime();
|
||||
case LIGHT:
|
||||
return config.getLightVehicleCrossingTime();
|
||||
case HEAVY:
|
||||
return config.getHeavyVehicleCrossingTime();
|
||||
default:
|
||||
return 2.0; // Default fallback
|
||||
}
|
||||
return switch (type) {
|
||||
case BIKE -> config.getBikeVehicleCrossingTime();
|
||||
case LIGHT -> config.getLightVehicleCrossingTime();
|
||||
case HEAVY -> config.getHeavyVehicleCrossingTime();
|
||||
default -> 2.0;
|
||||
}; // Default fallback
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
142
main/src/main/java/sd/model/Message.java
Normal file
142
main/src/main/java/sd/model/Message.java
Normal file
@@ -0,0 +1,142 @@
|
||||
package sd.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a message exchanged between processes in the distributed simulation.
|
||||
* Each message has a unique ID, a type, a sender, a destination, and a payload.
|
||||
* This class implements {@link Serializable} to allow transmission over the network.
|
||||
*/
|
||||
public class Message implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Unique identifier for this message.
|
||||
*/
|
||||
private final String messageId;
|
||||
|
||||
/**
|
||||
* The type of this message (e.g., VEHICLE_TRANSFER, STATS_UPDATE).
|
||||
*/
|
||||
private final MessageType type;
|
||||
|
||||
/**
|
||||
* Identifier of the process that sent this message.
|
||||
*/
|
||||
private final String senderId;
|
||||
|
||||
/**
|
||||
* Identifier of the destination process. Can be null for broadcast messages.
|
||||
*/
|
||||
private final String destinationId;
|
||||
|
||||
/**
|
||||
* The actual data being transmitted. Type depends on the message type.
|
||||
*/
|
||||
private final Object payload;
|
||||
|
||||
/**
|
||||
* Timestamp when this message was created (simulation time or real time).
|
||||
*/
|
||||
private final long timestamp;
|
||||
|
||||
/**
|
||||
* Creates a new message with all parameters.
|
||||
*
|
||||
* @param type The message type
|
||||
* @param senderId The ID of the sending process
|
||||
* @param destinationId The ID of the destination process (null for broadcast)
|
||||
* @param payload The message payload
|
||||
* @param timestamp The timestamp of message creation
|
||||
*/
|
||||
public Message(MessageType type, String senderId, String destinationId,
|
||||
Object payload, long timestamp) {
|
||||
this.messageId = UUID.randomUUID().toString();
|
||||
this.type = type;
|
||||
this.senderId = senderId;
|
||||
this.destinationId = destinationId;
|
||||
this.payload = payload;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new message with current system time as timestamp.
|
||||
*
|
||||
* @param type The message type
|
||||
* @param senderId The ID of the sending process
|
||||
* @param destinationId The ID of the destination process
|
||||
* @param payload The message payload
|
||||
*/
|
||||
public Message(MessageType type, String senderId, String destinationId, Object payload) {
|
||||
this(type, senderId, destinationId, payload, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a broadcast message (no specific destination).
|
||||
*
|
||||
* @param type The message type
|
||||
* @param senderId The ID of the sending process
|
||||
* @param payload The message payload
|
||||
*/
|
||||
public Message(MessageType type, String senderId, Object payload) {
|
||||
this(type, senderId, null, payload, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
//Getters
|
||||
|
||||
public String getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public MessageType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getSenderId() {
|
||||
return senderId;
|
||||
}
|
||||
|
||||
public String getDestinationId() {
|
||||
return destinationId;
|
||||
}
|
||||
|
||||
public Object getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this is a broadcast message (no specific destination).
|
||||
*
|
||||
* @return true if destinationId is null, false otherwise
|
||||
*/
|
||||
public boolean isBroadcast() {
|
||||
return destinationId == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the payload cast to a specific type.
|
||||
* Use with caution and ensure type safety.
|
||||
*
|
||||
* @param <T> The expected payload type
|
||||
* @return The payload cast to type T
|
||||
* @throws ClassCastException if the payload is not of type T
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getPayloadAs(Class<T> clazz) {
|
||||
return (T) payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Message[id=%s, type=%s, from=%s, to=%s, timestamp=%d]",
|
||||
messageId, type, senderId,
|
||||
destinationId != null ? destinationId : "BROADCAST",
|
||||
timestamp);
|
||||
}
|
||||
}
|
||||
81
main/src/main/java/sd/model/MessageType.java
Normal file
81
main/src/main/java/sd/model/MessageType.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package sd.model;
|
||||
|
||||
/**
|
||||
* Enumeration representing all possible message types for distributed communication.
|
||||
* These types are used for inter-process communication between different components
|
||||
* of the distributed traffic simulation system.
|
||||
*/
|
||||
public enum MessageType {
|
||||
|
||||
/**
|
||||
* Message to transfer a vehicle between intersections or processes.
|
||||
* Payload: Vehicle object with current state
|
||||
*/
|
||||
VEHICLE_TRANSFER,
|
||||
|
||||
/**
|
||||
* Message to update statistics across the distributed system.
|
||||
* Payload: Statistics data (waiting times, queue sizes, etc.)
|
||||
*/
|
||||
STATS_UPDATE,
|
||||
|
||||
/**
|
||||
* Message to synchronize traffic light states between processes.
|
||||
* Payload: TrafficLight state and timing information
|
||||
*/
|
||||
TRAFFIC_LIGHT_SYNC,
|
||||
|
||||
/**
|
||||
* Heartbeat message to check if a process is alive.
|
||||
* Payload: Process ID and timestamp
|
||||
*/
|
||||
HEARTBEAT,
|
||||
|
||||
/**
|
||||
* Request to join the distributed simulation.
|
||||
* Payload: Process information and capabilities
|
||||
*/
|
||||
JOIN_REQUEST,
|
||||
|
||||
/**
|
||||
* Response to a join request.
|
||||
* Payload: Acceptance status and configuration
|
||||
*/
|
||||
JOIN_RESPONSE,
|
||||
|
||||
/**
|
||||
* Message to notify about a new vehicle generation.
|
||||
* Payload: Vehicle generation parameters
|
||||
*/
|
||||
VEHICLE_SPAWN,
|
||||
|
||||
/**
|
||||
* Message to request the current state of an intersection.
|
||||
* Payload: Intersection ID
|
||||
*/
|
||||
STATE_REQUEST,
|
||||
|
||||
/**
|
||||
* Response containing the current state of an intersection.
|
||||
* Payload: Complete intersection state
|
||||
*/
|
||||
STATE_RESPONSE,
|
||||
|
||||
/**
|
||||
* Message to signal shutdown of a process.
|
||||
* Payload: Process ID and reason
|
||||
*/
|
||||
SHUTDOWN,
|
||||
|
||||
/**
|
||||
* Acknowledgment message for reliable communication.
|
||||
* Payload: Message ID being acknowledged
|
||||
*/
|
||||
ACK,
|
||||
|
||||
/**
|
||||
* Error message to report problems in the distributed system.
|
||||
* Payload: Error description and context
|
||||
*/
|
||||
ERROR
|
||||
}
|
||||
114
main/src/main/java/sd/serialization/JsonMessageSerializer.java
Normal file
114
main/src/main/java/sd/serialization/JsonMessageSerializer.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package sd.serialization;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* JSON-based implementation of {@link MessageSerializer} using Google's Gson library.
|
||||
*
|
||||
* This serializer converts objects to JSON format for transmission, providing:
|
||||
* - Human-readable message format (easy debugging)
|
||||
* - Cross-platform compatibility
|
||||
* - Smaller message sizes compared to Java native serialization
|
||||
* - Better security (no code execution during deserialization)
|
||||
*
|
||||
* The serializer is configured with pretty printing disabled by default for
|
||||
* production use, but can be enabled for debugging purposes.
|
||||
*
|
||||
* Thread-safety: This class is thread-safe as Gson instances are thread-safe.
|
||||
*
|
||||
* @see MessageSerializer
|
||||
*/
|
||||
public class JsonMessageSerializer implements MessageSerializer {
|
||||
|
||||
private final Gson gson;
|
||||
private final boolean prettyPrint;
|
||||
|
||||
/**
|
||||
* Creates a new JSON serializer with default configuration (no pretty printing).
|
||||
*/
|
||||
public JsonMessageSerializer() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new JSON serializer with optional pretty printing.
|
||||
*
|
||||
* @param prettyPrint If true, JSON output will be formatted with indentation
|
||||
*/
|
||||
public JsonMessageSerializer(boolean prettyPrint) {
|
||||
this.prettyPrint = prettyPrint;
|
||||
GsonBuilder builder = new GsonBuilder();
|
||||
|
||||
if (prettyPrint) {
|
||||
builder.setPrettyPrinting();
|
||||
}
|
||||
|
||||
// Register custom type adapters here if needed
|
||||
// builder.registerTypeAdapter(Vehicle.class, new VehicleAdapter());
|
||||
|
||||
this.gson = builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(Object object) throws SerializationException {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException("Cannot serialize null object");
|
||||
}
|
||||
|
||||
try {
|
||||
String json = gson.toJson(object);
|
||||
return json.getBytes(StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
throw new SerializationException(
|
||||
"Failed to serialize object of type " + object.getClass().getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T deserialize(byte[] data, Class<T> clazz) throws SerializationException {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Cannot deserialize null data");
|
||||
}
|
||||
if (clazz == null) {
|
||||
throw new IllegalArgumentException("Class type cannot be null");
|
||||
}
|
||||
|
||||
try {
|
||||
String json = new String(data, StandardCharsets.UTF_8);
|
||||
return gson.fromJson(json, clazz);
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new SerializationException(
|
||||
"Failed to parse JSON for type " + clazz.getName(), e);
|
||||
} catch (Exception e) {
|
||||
throw new SerializationException(
|
||||
"Failed to deserialize object of type " + clazz.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "JSON (Gson)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying Gson instance for advanced usage.
|
||||
*
|
||||
* @return The Gson instance
|
||||
*/
|
||||
public Gson getGson() {
|
||||
return gson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if pretty printing is enabled.
|
||||
*
|
||||
* @return true if pretty printing is enabled
|
||||
*/
|
||||
public boolean isPrettyPrint() {
|
||||
return prettyPrint;
|
||||
}
|
||||
}
|
||||
48
main/src/main/java/sd/serialization/MessageSerializer.java
Normal file
48
main/src/main/java/sd/serialization/MessageSerializer.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package sd.serialization;
|
||||
|
||||
/**
|
||||
* Interface for serializing and deserializing objects for network transmission.
|
||||
*
|
||||
* This interface provides a common abstraction for different serialization strategies
|
||||
* allowing the system to switch between implementations without changing the communication layer.
|
||||
*
|
||||
* Implementations must ensure:
|
||||
* - Thread-safety if used in concurrent contexts
|
||||
* - Proper exception handling with meaningful error messages
|
||||
* - Preservation of object state during round-trip serialization
|
||||
*
|
||||
* @see JsonMessageSerializer
|
||||
*/
|
||||
public interface MessageSerializer {
|
||||
|
||||
/**
|
||||
* Serializes an object into a byte array for transmission.
|
||||
*
|
||||
* @param object The object to serialize (must not be null)
|
||||
* @return A byte array containing the serialized representation
|
||||
* @throws SerializationException If serialization fails
|
||||
* @throws IllegalArgumentException If object is null
|
||||
*/
|
||||
byte[] serialize(Object object) throws SerializationException;
|
||||
|
||||
/**
|
||||
* Deserializes a byte array back into an object of the specified type.
|
||||
*
|
||||
* @param <T> The expected type of the deserialized object
|
||||
* @param data The byte array containing serialized data (must not be null)
|
||||
* @param clazz The class of the expected object type (must not be null)
|
||||
* @return The deserialized object
|
||||
* @throws SerializationException If deserialization fails
|
||||
* @throws IllegalArgumentException If data or clazz is null
|
||||
*/
|
||||
<T> T deserialize(byte[] data, Class<T> clazz) throws SerializationException;
|
||||
|
||||
/**
|
||||
* Gets the name of this serialization strategy (e.g., "JSON", "Java Native").
|
||||
* Useful for logging and debugging.
|
||||
*
|
||||
* @return The serializer name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
}
|
||||
134
main/src/main/java/sd/serialization/SerializationExample.java
Normal file
134
main/src/main/java/sd/serialization/SerializationExample.java
Normal file
@@ -0,0 +1,134 @@
|
||||
package sd.serialization;
|
||||
|
||||
import sd.model.Message;
|
||||
import sd.model.MessageType;
|
||||
import sd.model.Vehicle;
|
||||
import sd.model.VehicleType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Demonstration of JSON serialization usage in the traffic simulation system.
|
||||
*
|
||||
* This class shows practical examples of how to use JSON (Gson) serialization
|
||||
* for network communication between simulation processes.
|
||||
*/
|
||||
public class SerializationExample {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== JSON Serialization Example ===\n");
|
||||
|
||||
// Create a sample vehicle
|
||||
List<String> route = Arrays.asList("Cr1", "Cr2", "Cr5", "S");
|
||||
Vehicle vehicle = new Vehicle("V001", VehicleType.LIGHT, 10.5, route);
|
||||
vehicle.addWaitingTime(2.3);
|
||||
vehicle.addCrossingTime(1.2);
|
||||
|
||||
// Create a message containing the vehicle
|
||||
Message message = new Message(
|
||||
MessageType.VEHICLE_TRANSFER,
|
||||
"Cr1",
|
||||
"Cr2",
|
||||
vehicle
|
||||
);
|
||||
|
||||
// ===== JSON Serialization =====
|
||||
demonstrateJsonSerialization(message);
|
||||
|
||||
// ===== Factory Usage =====
|
||||
demonstrateFactoryUsage(message);
|
||||
|
||||
// ===== Performance Test =====
|
||||
performanceTest(message);
|
||||
}
|
||||
|
||||
private static void demonstrateJsonSerialization(Message message) {
|
||||
System.out.println("--- JSON Serialization ---");
|
||||
|
||||
try {
|
||||
// Create JSON serializer with pretty printing for readability
|
||||
MessageSerializer serializer = new JsonMessageSerializer(true);
|
||||
|
||||
// Serialize to bytes
|
||||
byte[] data = serializer.serialize(message);
|
||||
|
||||
// Display the JSON
|
||||
String json = new String(data);
|
||||
System.out.println("Serialized JSON (" + data.length + " bytes):");
|
||||
System.out.println(json);
|
||||
|
||||
// Deserialize back
|
||||
Message deserialized = serializer.deserialize(data, Message.class);
|
||||
System.out.println("\nDeserialized: " + deserialized);
|
||||
System.out.println("✓ JSON serialization successful\n");
|
||||
|
||||
} catch (SerializationException e) {
|
||||
System.err.println("❌ JSON serialization failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void demonstrateFactoryUsage(Message message) {
|
||||
System.out.println("--- Using SerializerFactory ---");
|
||||
|
||||
try {
|
||||
// Get default serializer (JSON)
|
||||
MessageSerializer serializer = SerializerFactory.createDefault();
|
||||
System.out.println("Default serializer: " + serializer.getName());
|
||||
|
||||
// Use it
|
||||
byte[] data = serializer.serialize(message);
|
||||
Message deserialized = serializer.deserialize(data, Message.class);
|
||||
|
||||
System.out.println("Message type: " + deserialized.getType());
|
||||
System.out.println("From: " + deserialized.getSenderId() +
|
||||
" → To: " + deserialized.getDestinationId());
|
||||
System.out.println("✓ Factory usage successful\n");
|
||||
|
||||
} catch (SerializationException e) {
|
||||
System.err.println("❌ Factory usage failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void performanceTest(Message message) {
|
||||
System.out.println("--- Performance Test ---");
|
||||
|
||||
int iterations = 1000;
|
||||
|
||||
try {
|
||||
MessageSerializer compactSerializer = new JsonMessageSerializer(false);
|
||||
MessageSerializer prettySerializer = new JsonMessageSerializer(true);
|
||||
|
||||
// Warm up
|
||||
for (int i = 0; i < 100; i++) {
|
||||
compactSerializer.serialize(message);
|
||||
}
|
||||
|
||||
// Test compact JSON
|
||||
long compactStart = System.nanoTime();
|
||||
byte[] compactData = null;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
compactData = compactSerializer.serialize(message);
|
||||
}
|
||||
long compactTime = System.nanoTime() - compactStart;
|
||||
|
||||
// Test pretty JSON
|
||||
byte[] prettyData = prettySerializer.serialize(message);
|
||||
|
||||
// Results
|
||||
System.out.println("Iterations: " + iterations);
|
||||
System.out.println("\nJSON Compact:");
|
||||
System.out.println(" Size: " + compactData.length + " bytes");
|
||||
System.out.println(" Time: " + (compactTime / 1_000_000.0) + " ms total");
|
||||
System.out.println(" Avg: " + (compactTime / iterations / 1_000.0) + " μs/operation");
|
||||
|
||||
System.out.println("\nJSON Pretty-Print:");
|
||||
System.out.println(" Size: " + prettyData.length + " bytes");
|
||||
System.out.println(" Size increase: " +
|
||||
String.format("%.1f%%", ((double)prettyData.length / compactData.length - 1) * 100));
|
||||
|
||||
} catch (SerializationException e) {
|
||||
System.err.println("❌ Performance test failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package sd.serialization;
|
||||
|
||||
/**
|
||||
* Exception thrown when serialization or deserialization operations fail.
|
||||
*
|
||||
* This exception wraps underlying errors (I/O exceptions, parsing errors, etc.)
|
||||
* and provides context about what went wrong during the serialization process.
|
||||
*/
|
||||
public class SerializationException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L; // Long(64bits) instead of int(32bits)
|
||||
|
||||
/**
|
||||
* Constructs a new serialization exception with the specified detail message.
|
||||
*
|
||||
* @param message The detail message
|
||||
*/
|
||||
public SerializationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new serialization exception with the specified detail message
|
||||
* and cause.
|
||||
*
|
||||
* @param message The detail message
|
||||
* @param cause The cause of this exception
|
||||
*/
|
||||
public SerializationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new serialization exception with the specified cause.
|
||||
*
|
||||
* @param cause The cause of this exception
|
||||
*/
|
||||
public SerializationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
66
main/src/main/java/sd/serialization/SerializerFactory.java
Normal file
66
main/src/main/java/sd/serialization/SerializerFactory.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package sd.serialization;
|
||||
|
||||
/**
|
||||
* Factory for creating {@link MessageSerializer} instances.
|
||||
*
|
||||
* This factory provides a centralized way to create and configure JSON serializers
|
||||
* using Gson, making it easy to configure serialization throughout the application.
|
||||
*
|
||||
* The factory can be configured via system properties for easy deployment configuration.
|
||||
*
|
||||
* Example usage:
|
||||
* <pre>
|
||||
* MessageSerializer serializer = SerializerFactory.createDefault();
|
||||
* byte[] data = serializer.serialize(myObject);
|
||||
* </pre>
|
||||
*/
|
||||
public class SerializerFactory {
|
||||
|
||||
/**
|
||||
* System property key for enabling pretty-print in JSON serialization.
|
||||
* Set to "true" for debugging, "false" for production.
|
||||
*/
|
||||
public static final String JSON_PRETTY_PRINT_PROPERTY = "sd.serialization.json.prettyPrint";
|
||||
|
||||
// Default configuration
|
||||
private static final boolean DEFAULT_JSON_PRETTY_PRINT = false;
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation.
|
||||
*/
|
||||
private SerializerFactory() {
|
||||
throw new UnsupportedOperationException("Factory class cannot be instantiated");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON serializer based on system configuration.
|
||||
*
|
||||
* Pretty-print is determined by checking the system property
|
||||
* {@value #JSON_PRETTY_PRINT_PROPERTY}. If not set, defaults to false.
|
||||
*
|
||||
* @return A configured JsonMessageSerializer instance
|
||||
*/
|
||||
public static MessageSerializer createDefault() {
|
||||
boolean prettyPrint = Boolean.getBoolean(JSON_PRETTY_PRINT_PROPERTY);
|
||||
return new JsonMessageSerializer(prettyPrint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON serializer with default configuration (no pretty printing).
|
||||
*
|
||||
* @return A JsonMessageSerializer instance
|
||||
*/
|
||||
public static MessageSerializer createSerializer() {
|
||||
return createSerializer(DEFAULT_JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON serializer with specified pretty-print setting.
|
||||
*
|
||||
* @param prettyPrint Whether to enable pretty printing
|
||||
* @return A JsonMessageSerializer instance
|
||||
*/
|
||||
public static MessageSerializer createSerializer(boolean prettyPrint) {
|
||||
return new JsonMessageSerializer(prettyPrint);
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class SimulationTest {
|
||||
assertEquals("TEST1", vehicle.getId());
|
||||
assertNotNull(vehicle.getType());
|
||||
assertNotNull(vehicle.getRoute());
|
||||
assertTrue(vehicle.getRoute().size() > 0);
|
||||
assertTrue(!vehicle.getRoute().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
140
main/src/test/java/sd/serialization/SerializationTest.java
Normal file
140
main/src/test/java/sd/serialization/SerializationTest.java
Normal file
@@ -0,0 +1,140 @@
|
||||
package sd.serialization;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import sd.model.Message;
|
||||
import sd.model.Vehicle;
|
||||
import sd.model.VehicleType;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Test suite for JSON serialization.
|
||||
*
|
||||
* Tests JSON serialization to ensure:
|
||||
* - Correct serialization and deserialization
|
||||
* - Data integrity during round-trip conversion
|
||||
* - Proper error handling
|
||||
*/
|
||||
class SerializationTest {
|
||||
|
||||
private MessageSerializer jsonSerializer = new JsonMessageSerializer();
|
||||
|
||||
private Vehicle testVehicle = new Vehicle("V001", VehicleType.LIGHT, 10.5,
|
||||
Arrays.asList("Cr1", "Cr2", "Cr5", "S"));
|
||||
private Message testMessage = new Message(
|
||||
sd.model.MessageType.VEHICLE_TRANSFER,
|
||||
"Cr1",
|
||||
"Cr2",
|
||||
testVehicle
|
||||
);
|
||||
|
||||
|
||||
// ===== JSON Serialization Tests =====
|
||||
|
||||
@Test
|
||||
@DisplayName("JSON: Should serialize and deserialize Vehicle correctly")
|
||||
void testJsonVehicleRoundTrip() throws SerializationException {
|
||||
// Serialize
|
||||
byte[] data = jsonSerializer.serialize(testVehicle);
|
||||
assertNotNull(data);
|
||||
assertTrue(data.length > 0);
|
||||
|
||||
// Print JSON for inspection
|
||||
System.out.println("JSON Vehicle:");
|
||||
System.out.println(new String(data));
|
||||
|
||||
// Deserialize
|
||||
Vehicle deserialized = jsonSerializer.deserialize(data, Vehicle.class);
|
||||
|
||||
// Verify
|
||||
assertNotNull(deserialized);
|
||||
assertEquals(testVehicle.getId(), deserialized.getId());
|
||||
assertEquals(testVehicle.getType(), deserialized.getType());
|
||||
assertEquals(testVehicle.getEntryTime(), deserialized.getEntryTime());
|
||||
assertEquals(testVehicle.getRoute(), deserialized.getRoute());
|
||||
assertEquals(testVehicle.getTotalWaitingTime(), deserialized.getTotalWaitingTime());
|
||||
assertEquals(testVehicle.getTotalCrossingTime(), deserialized.getTotalCrossingTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("JSON: Should serialize and deserialize Message correctly")
|
||||
void testJsonMessageRoundTrip() throws SerializationException {
|
||||
// Serialize
|
||||
byte[] data = jsonSerializer.serialize(testMessage);
|
||||
assertNotNull(data);
|
||||
|
||||
// Print JSON for inspection
|
||||
System.out.println("\nJSON Message:");
|
||||
System.out.println(new String(data));
|
||||
|
||||
// Deserialize
|
||||
Message deserialized = jsonSerializer.deserialize(data, Message.class);
|
||||
|
||||
// Verify
|
||||
assertNotNull(deserialized);
|
||||
assertEquals(testMessage.getType(), deserialized.getType());
|
||||
assertEquals(testMessage.getSenderId(), deserialized.getSenderId());
|
||||
assertEquals(testMessage.getDestinationId(), deserialized.getDestinationId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("JSON: Should throw exception on null object")
|
||||
void testJsonSerializeNull() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
jsonSerializer.serialize(null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("JSON: Should throw exception on null data")
|
||||
void testJsonDeserializeNull() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
jsonSerializer.deserialize(null, Vehicle.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("JSON: Should throw exception on invalid JSON")
|
||||
void testJsonDeserializeInvalid() {
|
||||
byte[] invalidData = "{ invalid json }".getBytes();
|
||||
assertThrows(SerializationException.class, () -> {
|
||||
jsonSerializer.deserialize(invalidData, Vehicle.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("JSON: Should preserve data integrity for complex objects")
|
||||
void testDataIntegrity() throws SerializationException {
|
||||
// Create a more complex vehicle
|
||||
Vehicle vehicle = new Vehicle("V999", VehicleType.HEAVY, 100.5,
|
||||
Arrays.asList("Cr1", "Cr2", "Cr3", "Cr4", "Cr5", "S"));
|
||||
vehicle.addWaitingTime(10.5);
|
||||
vehicle.addWaitingTime(5.3);
|
||||
vehicle.addCrossingTime(2.1);
|
||||
vehicle.advanceRoute();
|
||||
vehicle.advanceRoute();
|
||||
|
||||
// Serialize and deserialize
|
||||
byte[] jsonData = jsonSerializer.serialize(vehicle);
|
||||
Vehicle deserialized = jsonSerializer.deserialize(jsonData, Vehicle.class);
|
||||
|
||||
// Verify all fields match
|
||||
assertEquals(vehicle.getId(), deserialized.getId());
|
||||
assertEquals(vehicle.getType(), deserialized.getType());
|
||||
assertEquals(vehicle.getTotalWaitingTime(), deserialized.getTotalWaitingTime());
|
||||
assertEquals(vehicle.getCurrentRouteIndex(), deserialized.getCurrentRouteIndex());
|
||||
}
|
||||
|
||||
// ===== Factory Tests =====
|
||||
|
||||
@Test
|
||||
@DisplayName("Factory: Should create JSON serializer by default")
|
||||
void testFactoryDefault() {
|
||||
MessageSerializer serializer = SerializerFactory.createDefault();
|
||||
assertNotNull(serializer);
|
||||
assertEquals("JSON (Gson)", serializer.getName());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user