mirror of
https://github.com/davidalves04/Trabalho-Pratico-SD.git
synced 2025-12-08 12:33:31 +00:00
Merge pull request #27 from davidalves04/9-design-message-protocol-specification
#10 Req
This commit is contained in:
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
|
||||||
21
main/pom.xml
21
main/pom.xml
@@ -22,6 +22,27 @@
|
|||||||
<version>5.10.0</version>
|
<version>5.10.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Gson for JSON serialization -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
<version>2.10.1</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</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>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
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