diff --git a/.gitignore b/.gitignore index 237cb8c..cdddd14 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,13 @@ /pitiupi/build/ /pitiupi/dist/ +/pitiupi/plugins/*.jar /pitiupi.chat/build/ /pitiupi.chat/dist/ /pitiupi.chat/nbproject/private/ /pitiupi.screenCast/build/ /pitiupi.screenCast/dist/ +/pitiupi.mpChat/out/ +/pitiupi.mpChat/dist/ +**/built-jar.properties .idea/ diff --git a/README.md b/README.md index e2b66f8..90788b5 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,50 @@ # pitiupi -Ferramenta p2p para práticas em rede +Ferramenta **P2P** para práticas de Redes de Computadores: núcleo em Java com arquitetura de **plugins** (chat, compartilhamento de tela, exemplos multi-protocolo). -Este repositório tem 3 projetos Java que são independentes: -- A ferramenta pitiupi (P2P) -- Um plugin de Chat -- Um plugin de ScreenCast +**Documentação completa:** [docs/README.md](docs/README.md) -Cada um destes projetos deve ser compilado individualmente. Para isso, use a ferramenta ant no diretório do projeto. -Ex: +## Pré-requisitos + +| Ferramenta | Uso | +| --- | --- | +| **JDK** com `javac` | Compilação (OpenJDK **25** recomendado; núcleo compila com target **17**) | +| **Apache Ant** 1.10+ | `pitiupi/`, `pitiupi.chat/`, `pitiupi.screenCast/` | +| **GNU Make** | `pitiupi.mpChat/` | + +Exemplo Ubuntu / WSL: + +```bash +sudo apt install openjdk-25-jdk ant make +javac -version # deve existir — JRE sozinho não basta ``` -cd pitiupi.chat -ant jar -``` -Feito isso, os arquivos jar dos plugins deverão ser copiados ou linkados na pasta plugin da ferramenta. -No caso de a mesma ser linkada, ao recompilar os plugins não é necessário copiar o mesmo novamente para a pasta de plugins da ferramenta. -Para criar links simbólicos no Linux, use o comando: +## Módulos -``` +| Pasta | Descrição | Build | +| --- | --- | --- | +| `pitiupi/` | Núcleo (GUI, UDP multicast, TCP, heartbeat) | `ant jar` | +| `pitiupi.chat/` | Plugin referência — chat | `ant jar` | +| `pitiupi.screenCast/` | Plugin referência — screen cast | `ant jar` | +| `pitiupi.mpChat/` | Plugin referência — chat multicast + TCP | `make` | + +Compile cada módulo no seu diretório (núcleo primeiro). Instale os JARs dos plugins em `pitiupi/plugins/` — ver [pitiupi/plugins/README.md](pitiupi/plugins/README.md). + +Exemplo de symlinks (Linux): + +```bash cd pitiupi/plugins -ln -s ../../pitiupi.chat/dist/*.jar . -ln -s ../../pitiupi.screenCast/dist/*.jar . -``` -Isso deve resolver! +ln -sf ../../pitiupi.chat/dist/pitiupi.chat.jar pitiupi.chat.jar +ln -sf ../../pitiupi.screenCast/dist/pitiupi.screenCast.jar pitiupi.screenCast.jar +ln -sf ../../pitiupi.mpChat/dist/mpChat.jar mpChat.jar +``` -Para criar um novo plugin: +Execute o núcleo (a partir de `pitiupi/`): +```bash +cd pitiupi +java -jar dist/pitiupi.jar +``` + +Quickstart: [docs/documentation/00_quickstart.md](docs/documentation/00_quickstart.md). +Trabalho prático (plugin próprio): [docs/documentation/10_tutorial_plugin.md](docs/documentation/10_tutorial_plugin.md). diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..d5f2fbf --- /dev/null +++ b/docs/README.md @@ -0,0 +1,35 @@ +# Documentação — Pitiupi + +Entrada na raiz do repositório: [README.md](../README.md). + +## Pilares + +| Pasta / arquivo | Papel | +| --- | --- | +| **[documentation/](documentation/README.md)** | Código, rede, plugins, operação e guia para alunos | +| **[references/](references/README.md)** | Bibliografia e notas de conceitos (UDP, TCP, SPI) | +| **[decision-register.md](decision-register.md)** | Decisões de desenho da plataforma | + +## Público-alvo + +| Leitor | Começar por | +| --- | --- | +| **Aluno (TP)** | [documentation/00_quickstart.md](documentation/00_quickstart.md) → [10_tutorial_plugin.md](documentation/10_tutorial_plugin.md) → [11_plugins_referencia.md](documentation/11_plugins_referencia.md) | +| **Monitor / docência** | [documentation/01_visao_disciplina_e_tp.md](documentation/01_visao_disciplina_e_tp.md) → [15_testes_e_gate.md](documentation/15_testes_e_gate.md) | +| **Desenvolvedor** | [documentation/13_guia_dev_e_agente.md](documentation/13_guia_dev_e_agente.md) → [14_arquitetura_e_manutencao.md](documentation/14_arquitetura_e_manutencao.md) | +| **Professor / banca** | [documentation/16_fundamentacao_redes.md](documentation/16_fundamentacao_redes.md) → [references/README.md](references/README.md) | + +**Idioma:** documentação em PT-BR; pacotes e classes Java como no código. + +## Manutenção + +| Tipo de mudança | Onde documentar | +| --- | --- | +| Protocolo UDP/TCP, multicast, portas | [documentation/05_udp_multicast.md](documentation/05_udp_multicast.md), [06_tcp_conexoes.md](documentation/06_tcp_conexoes.md), [appendix_portas_e_multicast.md](documentation/appendix_portas_e_multicast.md) | +| Contrato de plugin / SPI | [documentation/09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md), [10_tutorial_plugin.md](documentation/10_tutorial_plugin.md) | +| Novo plugin de referência | [documentation/11_plugins_referencia.md](documentation/11_plugins_referencia.md) | +| Decisão de arquitetura | [decision-register.md](decision-register.md) | +| Débito técnico | [documentation/engineering_backlog.md](documentation/engineering_backlog.md) | +| Literatura de redes | [references/](references/) | + +Documentação legada em `pitiupi/docs/` redireciona para esta árvore. diff --git a/docs/decision-register.md b/docs/decision-register.md new file mode 100644 index 0000000..1f74759 --- /dev/null +++ b/docs/decision-register.md @@ -0,0 +1,14 @@ +# Registro de decisões — Pitiupi + +| ID | Decisão | Motivação | Doc | +| --- | --- | --- | --- | +| D1 | Grupo multicast `224.0.0.3` | Endereço fixo para descoberta e broadcast na LAN da sala | [appendix_portas_e_multicast.md](documentation/appendix_portas_e_multicast.md) | +| D2 | Mesma porta UDP e TCP por instância | Configurada no login; simplifica firewall em laboratório | [00_quickstart.md](documentation/00_quickstart.md) | +| D3 | Heartbeat via UDP multicast | Presença sem conexão TCP permanente com todos os peers | [08_heartbeat_e_presenca.md](documentation/08_heartbeat_e_presenca.md) | +| D4 | Timeout de peer ~15 s sem heartbeat | Remover usuários inativos da lista | [08_heartbeat_e_presenca.md](documentation/08_heartbeat_e_presenca.md) | +| D5 | Framing TCP: `int` tamanho + bytes | Delimitar mensagens em stream orientado a bytes | [06_tcp_conexoes.md](documentation/06_tcp_conexoes.md) | +| D6 | Mensagens `Serializable` em `byte[]` | Independência do transporte; plugins definem tipos | [07_mensagens_e_serializacao.md](documentation/07_mensagens_e_serializacao.md) | +| D7 | Plugins via JAR + Java SPI | Carregamento dinâmico sem recompilar o núcleo | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) | +| D8 | Classloader por JAR de plugin | Isolar dependências; desserialização com `PluginObjectInputStream` | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) | + +Novas decisões: adicionar linha nesta tabela e detalhar no arquivo técnico correspondente. diff --git a/docs/documentation/00_quickstart.md b/docs/documentation/00_quickstart.md new file mode 100644 index 0000000..c9cdb04 --- /dev/null +++ b/docs/documentation/00_quickstart.md @@ -0,0 +1,63 @@ +# Quickstart + +## Pré-requisitos + +- **JDK completo** (`java` e `javac`) — OpenJDK 25 recomendado; o núcleo usa `javac.source/target=17`, plugins Ant usam 25. +- Apache Ant (`pitiupi`, `pitiupi.chat`, `pitiupi.screenCast`) +- GNU Make (`pitiupi.mpChat`) + +Ubuntu / WSL: + +```bash +sudo apt install openjdk-25-jdk ant make +which javac ant make +``` + +Se `ant jar` falhar com *Cannot run program "javac"*, você instalou apenas JRE — instale `openjdk-*-jdk`. + +## Build + +Na raiz do clone: + +```bash +cd pitiupi && ant jar +cd ../pitiupi.chat && ant jar +cd ../pitiupi.screenCast && ant jar +cd ../pitiupi.mpChat && make +``` + +O núcleo gera `pitiupi/dist/pitiupi.jar`. Cada plugin gera JAR em seu `dist/`. + +## Instalar plugins + +A aplicação lê JARs de `pitiupi/plugins/` (relativo ao diretório de trabalho ao executar o núcleo). Detalhes: [pitiupi/plugins/README.md](../../pitiupi/plugins/README.md). + +Symlinks em desenvolvimento (Linux): + +```bash +cd pitiupi/plugins +ln -sf ../../pitiupi.chat/dist/pitiupi.chat.jar pitiupi.chat.jar +ln -sf ../../pitiupi.screenCast/dist/pitiupi.screenCast.jar pitiupi.screenCast.jar +ln -sf ../../pitiupi.mpChat/dist/mpChat.jar mpChat.jar +``` + +## Executar + +```bash +cd pitiupi +java -jar dist/pitiupi.jar +``` + +No diálogo inicial informe **nome** e **porta** (UDP/TCP). Para duas instâncias no mesmo computador, use **portas diferentes** (ex.: 5000 e 5001). + +## Verificar P2P + +1. Abra duas instâncias (mesma rede ou mesmo host com portas distintas). +2. Confirme que os nomes aparecem no painel de usuários online. +3. Teste um plugin de referência (ex.: chat em `MenuCategory.Social`). + +Problemas de multicast: [appendix_portas_e_multicast.md](appendix_portas_e_multicast.md). + +Gate de build (quatro módulos): [15_testes_e_gate.md](15_testes_e_gate.md). + +Próximo passo para o TP: [10_tutorial_plugin.md](10_tutorial_plugin.md). diff --git a/docs/documentation/01_visao_disciplina_e_tp.md b/docs/documentation/01_visao_disciplina_e_tp.md new file mode 100644 index 0000000..279ff8c --- /dev/null +++ b/docs/documentation/01_visao_disciplina_e_tp.md @@ -0,0 +1,47 @@ +# Visão da disciplina e trabalho prático + +## Papel do Pitiupi + +O Pitiupi é uma **plataforma P2P com plugins** usada em Redes de Computadores para: + +- Experimentar UDP (multicast/unicast) e TCP na mesma aplicação. +- Observar **presença** de peers (heartbeat) sem servidor central de aplicação. +- Desenvolver **trabalhos individuais ou em grupo** como novos plugins (jogos, ferramentas colaborativas, etc.). + +O núcleo fornece rede e GUI; o aluno implementa a lógica no plugin. + +## Trabalho típico (plugin) + +Cada equipe/aluno entrega um **módulo Java separado** que: + +1. Compila para JAR e carrega via `pitiupi/plugins/`. +2. Define mensagens (`extends Message`) e o fluxo entre peers. +3. Integra menus/janelas via `GuiManager`. +4. Inclui README do módulo (build + protocolo). + +Exemplos de tema: chat temático, tabuleiro (xadrez/damas), compartilhamento de arquivo, quiz síncrono. + +## Plugins de referência no repositório + +| Módulo | Ideia pedagógica | +| --- | --- | +| `pitiupi.chat` | Mensagens UDP + UI simples | +| `pitiupi.screenCast` | Payload maior / streaming de imagem | +| `pitiupi.mpChat` | Multicast + TCP privado no mesmo plugin | + +Detalhes: [11_plugins_referencia.md](11_plugins_referencia.md). + +## Critérios genéricos (ajustar na disciplina) + +| Critério | Indicador | +| --- | --- | +| Funcionalidade | Comportamento correto entre dois+ peers na rede da sala | +| Uso da plataforma | SPI, mensagens, sem “fork” desnecessário do núcleo | +| Build | `ant jar` / `make` reproduzível | +| Documentação | README do plugin + descrição das mensagens | + +## Para monitores + +- Roteiro de build: [00_quickstart.md](00_quickstart.md). +- Checklist de entrega: [15_testes_e_gate.md](15_testes_e_gate.md). +- Fundamentação para dúvidas de alunos: [16_fundamentacao_redes.md](16_fundamentacao_redes.md). diff --git a/docs/documentation/02_modulos_do_repositorio.md b/docs/documentation/02_modulos_do_repositorio.md new file mode 100644 index 0000000..43a9b3f --- /dev/null +++ b/docs/documentation/02_modulos_do_repositorio.md @@ -0,0 +1,34 @@ +# Módulos do repositório + +``` +pitiupi/ # clone (este repositório) +├── pitiupi/ # núcleo P2P (executável) +├── pitiupi.chat/ # plugin referência — chat +├── pitiupi.screenCast/ # plugin referência — captura de tela +├── pitiupi.mpChat/ # plugin referência — chat multi-protocolo +└── docs/ # documentação canônica +``` + +## Núcleo (`pitiupi/`) + +- **Build:** Apache Ant (`build.xml`, `nbproject/`). +- **Entrada:** `pitiupi.Main` → login → `MainWindow`. +- **Pacotes principais:** `pitiupi.net`, `pitiupi.control`, `pitiupi.GUI`, `pitiupi.models`. + +## Plugins de referência + +| Pasta | Build | JAR típico | Classe SPI | +| --- | --- | --- | --- | +| `pitiupi.chat` | `ant jar` | `dist/pitiupi.chat.jar` | `pitiupi.chat.plugin.ChatPlugin` | +| `pitiupi.screenCast` | `ant jar` | `dist/pitiupi.screenCast.jar` | `pitiupi.screenCast.plugin.ScreenCastPlugin` | +| `pitiupi.mpChat` | `make` | `dist/mpChat.jar` | `plugin.MultiProtocolChat` | + +Todos dependem do JAR do núcleo no classpath de compilação. + +## O que o aluno cria + +Novo diretório irmão, por exemplo `pitiupi.xadrez/`, copiando a estrutura de `pitiupi.chat` (Ant + `META-INF/services`). + +Não é obrigatório commitar o plugin do aluno neste repositório base; o professor pode definir fork ou entrega em zip. + +Ver [03_build_ant_netbeans.md](03_build_ant_netbeans.md) e [10_tutorial_plugin.md](10_tutorial_plugin.md). diff --git a/docs/documentation/03_build_ant_netbeans.md b/docs/documentation/03_build_ant_netbeans.md new file mode 100644 index 0000000..6de92c8 --- /dev/null +++ b/docs/documentation/03_build_ant_netbeans.md @@ -0,0 +1,49 @@ +# Build — Ant, NetBeans e Make + +## Pré-requisitos e versões Java + +| Módulo | Build | `javac.source` / `target` (NetBeans) | +| --- | --- | --- | +| `pitiupi` | Ant | 17 | +| `pitiupi.chat` | Ant | 25 | +| `pitiupi.screenCast` | Ant | 25 | +| `pitiupi.mpChat` | Make | (sem `project.properties`; compila contra `pitiupi.jar`) | + +Use um JDK que forneça `javac` (ex.: OpenJDK 25). Ant 1.10+ e Make no PATH. + +## Núcleo e plugins NetBeans + +Projetos com `nbproject/` e `build.xml`: + +```bash +cd pitiupi && ant jar +cd pitiupi.chat && ant jar +cd pitiupi.screenCast && ant jar +``` + +Abrir no NetBeans: **File → Open Project** em cada pasta. + +O target `jar` produz o executável do núcleo e os JARs dos plugins. O classpath de compilação dos plugins aponta para `../pitiupi/dist/pitiupi.jar` (ver `project.properties` de cada módulo). + +## pitiupi.mpChat (Makefile) + +```bash +cd pitiupi.mpChat +make +``` + +O Makefile compila com `javac -cp ../pitiupi/dist/pitiupi.jar`, copia `META-INF` e gera `dist/mpChat.jar`. **Compile o núcleo antes.** + +## Diretório `plugins/` + +A pasta `pitiupi/plugins/` contém os JARs carregados em runtime. Apenas arquivos `.jar` são lidos. Ver [pitiupi/plugins/README.md](../../pitiupi/plugins/README.md). + +## Ordem recomendada de build + +1. `pitiupi` (núcleo) +2. Plugins em qualquer ordem +3. Symlink ou cópia para `pitiupi/plugins/` + +## Gate automatizado (docentes) + +Quem mantém o meta-workspace Cursor pode usar o script de gate que executa `ant jar` nos três módulos Ant e `make` no mpChat (requer Ant/Make no PATH). Detalhes em [15_testes_e_gate.md](15_testes_e_gate.md). diff --git a/docs/documentation/04_inicializacao_e_gui.md b/docs/documentation/04_inicializacao_e_gui.md new file mode 100644 index 0000000..e21c2a5 --- /dev/null +++ b/docs/documentation/04_inicializacao_e_gui.md @@ -0,0 +1,76 @@ +# Visão geral + +O `Pitiupi` é uma plataforma P2P baseada em uma arquitetura de plugins, +desenvolvida para permitir a criação de aplicações distribuídas sobre uma +infraestrutura comum de comunicação em rede. + +A aplicação fornece mecanismos de descoberta de usuários, comunicação entre +peers, troca de mensagens serializadas e integração de extensões por meio de +plugins. A comunicação de rede utiliza dois protocolos de transporte: UDP, +empregado principalmente na descoberta e comunicação multicast entre as +instâncias, e TCP, utilizado para comunicação direta e persistente entre peers. + +O projeto foi desenvolvido como suporte às práticas da disciplina de Redes de +Computadores, servindo também como base para experimentação de aplicações +distribuídas e desenvolvimento de projetos acadêmicos. + +Índice: [README.md](README.md). + +# Arquitetura geral + +A aplicação é dividida em três grupos principais: + +- **Núcleo da aplicação**: a `MainWindow` coordena GUI, rede e plugins. +- **Sistema de plugins**: extensões em JAR carregadas de `pitiupi/plugins/`. +- **Comunicação de rede**: UDP (`SocketUDP`) e TCP (`SocketTCP`, `TcpConnection`). + +## Inicialização da aplicação + +1. A `MainWindow` é criada após o diálogo de login (nome e porta). +2. O `SocketUDP` associa-se à porta configurada e ingressa no grupo multicast. +3. O `SocketTCP` cria um `ServerSocket` na mesma porta para conexões TCP. +4. Os mecanismos de comunicação iniciam threads de recepção. +5. O `HeartbeatManager` envia heartbeats periodicamente. +6. O painel de usuários online registra-se como listener do heartbeat. +7. O `PluginManager` carrega os JARs em `plugins/` via SPI. + +Ver também: [05_udp_multicast.md](05_udp_multicast.md), [09_sistema_de_plugins.md](09_sistema_de_plugins.md). + +# Interface gráfica + +A interface gráfica da aplicação é baseada em Swing e é centralizada na +classe `MainWindow`. + +Durante a inicialização, cada plugin recebe `ConnectionManager` e `GuiManager` +no método `createPlugin()`. + +A extensão usual é registrar menus por categoria (`MenuCategory`). Ver +[12_menus_unicast_multicast.md](12_menus_unicast_multicast.md). + +## Diagrama de comunicação + +``` + +------------------+ + | MainWindow | + +--------+---------+ + | + +--------------------+--------------------+ + | | | + v v v + HeartbeatManager SocketUDP SocketTCP + | | | + v v v + Heartbeat UDP Multicast TCP Connections + | | + +---------+----------+ + | + v + Plugins + | + v + Message +``` + +O UDP multicast sustenta presença e mensagens broadcast/unicast; o TCP sustenta +canais diretos com framing de mensagens. Detalhes em [05](05_udp_multicast.md) +e [06](06_tcp_conexoes.md). diff --git a/docs/documentation/05_udp_multicast.md b/docs/documentation/05_udp_multicast.md new file mode 100644 index 0000000..d289f61 --- /dev/null +++ b/docs/documentation/05_udp_multicast.md @@ -0,0 +1,85 @@ +# Comunicação UDP + + +A classe `SocketUDP` é responsável pela comunicação UDP da aplicação. + +Ela utiliza um `MulticastSocket` associado à porta configurada pela aplicação e +ingressa no grupo multicast `224.0.0.3`. Por ser baseada em UDP, a comunicação não +estabelece uma conexão permanente entre os peers. + +A classe estende `Thread`, mantendo um processo contínuo de recepção de datagramas +enquanto a aplicação está em execução. + +O SocketUDP suporta dois modos de envio: + +- **multicast**, destinado ao grupo de peers +- **unicast**, destinado diretamente ao endereço IP de um peer específico + +### Inicialização + +Durante sua inicialização, o `SocketUDP`: + +1. obtém o endereço do grupo multicast; +2. cria um `MulticastSocket` utilizando a porta configurada na `MainWindow`; +3. configura os buffers de envio e recepção; +4. habilita o reuso do endereço; +5. ingressa no grupo multicast. + +O tamanho dos buffers de comunicação é configurado para `256000` bytes. + +### Envio de mensagens UDP + +O `SocketUDP` suporta dois modos de envio de mensagens: multicast e unicast, ambos +baseados no mesmo mecanismo interno de criação e envio de DatagramPacket. + +O método `sendMulticast()` envia uma mensagem para o grupo multicast da aplicação. +Nesse caso, o `DatagramPacket` é direcionado ao endereço multicast configurado, +permitindo que todas as instâncias participantes do grupo recebam a mensagem +simultaneamente. + +Já o método `sendUnicast()` permite enviar uma mensagem diretamente para um peer +específico. Nesse caso, o `DatagramPacket` é direcionado ao endereço IP informado pelo +chamador, mantendo a mesma porta utilizada pela aplicação. + +Ambos os métodos são, na prática, abstrações de um único mecanismo interno de envio, +que utiliza o mesmo processo de criação e envio de `DatagramPacket` por baixo dos +panos. A diferença entre eles está apenas no endereço de destino: enquanto o multicast +utiliza o grupo compartilhado, o unicast direciona o pacote a um IP específico. + +### Recepção de mensagens + +O método `receive()` mantém um loop aguardando novos datagramas. + +Ao receber uma mensagem, o `SocketUDP` inicialmente tenta identificar se os dados +correspondem a uma `HeartbeatMessage`. + +O processamento segue o seguinte fluxo: + +1. O datagrama é recebido pelo `MulticastSocket`. +2. Os dados recebidos são analisados pelo método `tryParseHeartbeat()`. +3. Caso seja identificada uma `HeartbeatMessage`, ela é encaminhada ao + `HeartbeatManager`, juntamente com o endereço IP de origem. +4. Caso não seja um heartbeat, a mensagem é encaminhada para todos os plugins + carregados. +5. Cada plugin decide se a mensagem recebida pertence à sua funcionalidade. + +Essa separação mantém as mensagens de infraestrutura, como heartbeat, sob +responsabilidade do núcleo da aplicação, enquanto as mensagens específicas das +funcionalidades são processadas pelos plugins. + +### Identificação de mensagens de heartbeat + +O método `tryParseHeartbeat()` tenta desserializar os dados recebidos e verificar se o +objeto resultante é uma instância de `HeartbeatMessage`. + +Caso a mensagem não possa ser desserializada ou não corresponda ao tipo esperado, o +método retorna `null`. Nesse caso, a mensagem é tratada como uma mensagem destinada aos +plugins. + +### Encerramento + +O método `close()` interrompe a execução do processo de recepção, remove a aplicação +do grupo multicast e fecha o `MulticastSocket`. + +A variável `running` é utilizada para sinalizar que o processo de recepção deve ser +encerrado. diff --git a/docs/documentation/06_tcp_conexoes.md b/docs/documentation/06_tcp_conexoes.md new file mode 100644 index 0000000..f088a22 --- /dev/null +++ b/docs/documentation/06_tcp_conexoes.md @@ -0,0 +1,192 @@ +# Comunicação TCP + + +A comunicação TCP é implementada pela classe `SocketTCP`. + +Diferentemente do UDP, o TCP estabelece conexões entre dois peers. Essas conexões +são mantidas enquanto forem necessárias e podem ser reutilizadas para o envio de +múltiplas mensagens. + +A arquitetura utiliza uma conexão TCP persistente por endereço de peer. + +O SocketTCP possui um mapa de conexões: + +`InetAddress` -> `TcpConnection` + +Esse mapa permite localizar rapidamente uma conexão existente para determinado peer e +reutilizá-la durante novos envios. + +### SocketTCP + +A classe `SocketTCP` estende `Thread` e implementa `PeerListener`. + +Ao ser criada, ela inicializa um `ServerSocket` utilizando a porta configurada na +aplicação. + +O servidor TCP permanece aguardando novas conexões enquanto a aplicação estiver +em execução. + +### Envio de mensagens TCP + +O método `send()` recebe uma mensagem serializada e o endereço IP do peer destinatário. + +Antes de enviar a mensagem, o `SocketTCP` verifica se já existe uma conexão válida com +o peer. + +O comportamento é: + +1. Procurar uma conexão existente no mapa de conexões. +2. Verificar se a conexão está fechada. +3. Caso não exista uma conexão válida, criar um novo Socket TCP para o endereço do peer. +4. Encapsular o socket em uma `TcpConnection`. +5. Registrar a conexão no mapa. +6. Iniciar uma tarefa de recepção para essa conexão. +7. Enviar a mensagem pela conexão. + +Assim, uma conexão TCP existente pode ser reutilizada para várias mensagens, evitando +a criação de um novo socket para cada envio. + +### Recepção de conexões + +O método `receive()` permanece aguardando novas conexões através de +`ServerSocket.accept()`. + +Quando uma conexão é aceita: + +O endereço IP do peer é obtido. +Um objeto `TcpConnection` é criado para encapsular o socket. +A conexão é registrada no mapa de conexões. +Caso já exista uma conexão associada ao mesmo endereço, a conexão anterior é encerrada. +Uma tarefa independente de recepção é criada para a nova conexão. + +Esse modelo permite que o servidor aceite múltiplas conexões sem bloquear o +processamento das conexões já existentes. + +### Recepção concorrente + +A recepção das mensagens TCP é realizada de maneira concorrente por meio de um +`ExecutorService`. + +Cada conexão possui uma tarefa própria responsável por receber continuamente as +mensagens. + +O fluxo de recepção é: + +``` +SocketTCP +| ++-- Peer A → TcpConnection → tarefa de recepção +| ++-- Peer B → TcpConnection → tarefa de recepção +| ++-- Peer C → TcpConnection → tarefa de recepção +``` + +Dessa forma, uma conexão bloqueada ou aguardando dados não impede que outras conexões +continuem sendo processadas. + +Quando uma mensagem é recebida por uma `TcpConnection`, ela é encaminhada para todos os +plugins registrados na `MainWindow`. + +O plugin responsável pela funcionalidade deve identificar o tipo da mensagem e +realizar o processamento correspondente. + +### TcpConnection + +A classe TcpConnection encapsula uma conexão TCP individual entre dois peers. + +Ela mantém: + +o Socket utilizado na conexão; +um DataInputStream para recepção; +um DataOutputStream para envio. + +Além de encapsular esses recursos, a classe é responsável pelo framing das mensagens. + +### Framing das mensagens TCP + +A classe `TcpConnection` encapsula uma conexão TCP individual entre dois peers. + +Ela mantém: + +1. o `Socket` utilizado na conexão; +2. um `DataInputStream` para recepção; +3. um `DataOutputStream` para envio. + +Além de encapsular esses recursos, a classe é responsável pelo framing das mensagens. + +Framing das mensagens TCP + +O protocolo TCP fornece um fluxo contínuo de bytes e não preserva os limites das +mensagens enviadas pela aplicação. + +Por esse motivo, é adicionado o tamanho da mensagem antes do conteúdo. + +O formato utilizado é: + +``` ++----------------------+----------------------+ +| tamanho da mensagem | dados da mensagem | ++----------------------+----------------------+ +4 bytes N bytes +``` + +O tamanho é enviado como um `int`, seguido pelos bytes da mensagem. + +No envio, o método `TcpConnection.send()`: + +1. obtém o tamanho do vetor de bytes; +2. escreve o tamanho no `DataOutputStream`; +3. escreve os dados da mensagem; +4. realiza `flush()` no stream. + +O acesso ao stream de saída é sincronizado para evitar que envios concorrentes +misturem seus dados. + +Na recepção, o método `receive()`: + +1. lê o tamanho da mensagem; +2. verifica se o tamanho é válido; +3. lê a quantidade correspondente de bytes; +4. retorna o vetor de bytes completo. + +Esse mecanismo permite que várias mensagens sejam transmitidas pela mesma conexão +TCP sem que o receptor perca a delimitação entre elas. + +### Integração entre TCP e gerenciamento de peers + +O `SocketTCP` implementa a interface PeerListener para acompanhar as alterações na +lista de peers ativos. + +O `HeartbeatManager` mantém a lista de peers atualmente detectados na rede. Quando +essa lista é alterada, o SocketTCP recebe a notificação por meio do método +`onPeersChanged()`. + +O método obtém os endereços IP dos peers atualmente ativos e compara esses endereços +com as conexões TCP existentes. + +Quando uma conexão pertence a um peer que não está mais ativo: + +a conexão é removida do mapa; +a conexão TCP é encerrada; +os recursos associados são liberados. + +Esse mecanismo impede que conexões TCP permaneçam abertas indefinidamente para peers +que já deixaram de participar da rede. + +O fluxo de integração pode ser representado da seguinte forma: +``` +HeartbeatManager +| +| peers ativos alterados +| +v +SocketTCP.onPeersChanged() +| +| compara peers ativos +| +v ++---- peer ativo ------> mantém conexão +| ++---- peer inativo ----> fecha conexão +``` diff --git a/docs/documentation/07_mensagens_e_serializacao.md b/docs/documentation/07_mensagens_e_serializacao.md new file mode 100644 index 0000000..59b7022 --- /dev/null +++ b/docs/documentation/07_mensagens_e_serializacao.md @@ -0,0 +1,18 @@ +# Mensagens + +### Message + +A classe `Message` representa a estrutura base das mensagens trocadas pela aplicação. + +Todas as mensagens utilizadas pelo sistema devem herdar dessa classe. Ela implementa +`Serializable`, permitindo que objetos de mensagem sejam convertidos em vetores de +bytes para transmissão pela rede. + +A serialização é realizada pelo método `toByteArray()`, que transforma uma instância +da mensagem em uma representação binária enviada pelos mecanismos de comunicação. + +As mensagens específicas devem estender essa classe, adicionando os atributos e +comportamentos necessários para cada funcionalidade. + +A camada de transporte não depende do conteúdo específico das mensagens. Tanto o +UDP quanto o TCP recebem mensagens serializadas como `byte[]`. diff --git a/docs/documentation/08_heartbeat_e_presenca.md b/docs/documentation/08_heartbeat_e_presenca.md new file mode 100644 index 0000000..e7cbcd3 --- /dev/null +++ b/docs/documentation/08_heartbeat_e_presenca.md @@ -0,0 +1,62 @@ +# Heartbeat + +### Heartbeat + +O mecanismo de heartbeat é utilizado para identificar quais usuários estão ativos +na rede. + +A aplicação envia periodicamente uma `HeartbeatMessage` contendo informações do usuário, +um identificador único da instância da aplicação e um timestamp. + +Essas mensagens são transmitidas utilizando a comunicação UDP multicast. + +Quando outra instância recebe uma mensagem de heartbeat, o `SocketUDP` identifica +o tipo da mensagem e encaminha o objeto ao `HeartbeatManager`, juntamente com o endereço +IP de origem. + +O `HeartbeatManager` registra ou atualiza o peer correspondente, utilizando o +identificador da instância para diferenciá-lo das demais. + +Um peer é considerado inativo quando permanece mais de 15 segundos sem receber um novo +heartbeat. O `HeartbeatManager` então remove o peer da lista e notifica todos os +componentes registrados como listeners de mudanças de peers. + +Esses listeners podem incluir diferentes partes da aplicação, como componentes +da interface gráfica, mecanismos de comunicação (como o `SocketTCP`) e também plugins +que desejem reagir à presença ou ausência de usuários na rede. + +Dessa forma, o sistema permite que tanto o núcleo da aplicação quanto extensões +externas sejam notificados sobre alterações no estado da rede, mantendo a arquitetura +flexível e extensível. + +O fluxo completo é: + +``` +HeartbeatManager +| +| cria HeartbeatMessage +| +v +Message.toByteArray() +| +v +SocketUDP.sendMulticast() +| +v +UDP Multicast +| +v +SocketUDP.receive() +| +v +HeartbeatManager.receiveHeartbeat() +| +v +Lista de peers ativos +| ++----> Interface de usuários online +| ++----> SocketTCP (PeerListener) +| ++----> Plugins (PeerListener) +``` diff --git a/docs/documentation/09_sistema_de_plugins.md b/docs/documentation/09_sistema_de_plugins.md new file mode 100644 index 0000000..c209a3b --- /dev/null +++ b/docs/documentation/09_sistema_de_plugins.md @@ -0,0 +1,60 @@ +# Sistema de plugins + +O sistema de plugins permite extensão modular sem alterar o núcleo. O contrato +está em `pitiupi.models.Plugin` (classe abstrata). + +## Interface Plugin + +Métodos obrigatórios: + +| Método | Função | +| --- | --- | +| `getName()`, `getAuthor()`, `getVersion()`, `getIdentifier()` | Identificação (o `identifier` distingue plugins em conexões TCP dedicadas) | +| `createPlugin(ConnectionManager, GuiManager)` | Inicialização: menus, janelas, uso da rede | +| `getMessages()` | Lista de classes `Message` que o plugin desserializa | +| `receiveMessage(Message)` | Processamento após o `PluginManager` identificar o tipo | + +Métodos opcionais (TCP avançado): + +| Método | Função | +| --- | --- | +| `receiveAllTypes()` | Receber todas as mensagens públicas | +| `receivePrivateConnection(TcpConnection)` | Aceitar conexão TCP direcionada ao plugin | +| `receiveBytes(TcpConnection, byte[])` | Ler payload bruto na conexão privada | +| `onSocketClosed(TcpConnection)` | Limpar estado ao fechar socket | + +### Descoberta de plugins + +Para serem reconhecidos, os plugins devem possuir os arquivos de configuração +do Java Service Provider Interface (SPI): + +``` +MeusPlugins +| ++---META-INF + | + +---services + | + +---pitiupi.models.Plugin +``` + +O arquivo `pitiupi.models.Plugin` deve conter o nome completo das classes que +implementam a interface Plugin: + +``` +MeusPlugins.plugins.NomeDaClasse +``` + +Para serem carregados, os plugins devem estar empacotados como arquivos JAR +e colocados no diretório plugins da aplicação. + +### Carregamento de plugins + +O `PluginManager.loadPlugins()` (chamado pela `MainWindow`): + +1. Lista arquivos `.jar` em `pitiupi/plugins/` (diretório de trabalho = pasta do núcleo). +2. Para cada JAR, cria um `URLClassLoader` e usa `ServiceLoader.load(Plugin.class, classLoader)`. +3. Chama `createPlugin(connectionManager, guiManager)` em cada instância. +4. Na recepção UDP/TCP, tenta desserializar com `PluginObjectInputStream` e despacha para plugins que declararam o tipo em `getMessages()`. + +Implementação: `pitiupi/src/pitiupi/control/plugins/PluginManager.java`. diff --git a/docs/documentation/10_tutorial_plugin.md b/docs/documentation/10_tutorial_plugin.md new file mode 100644 index 0000000..ee9b7bd --- /dev/null +++ b/docs/documentation/10_tutorial_plugin.md @@ -0,0 +1,75 @@ +# Tutorial — criar um plugin + +Este guia complementa [09_sistema_de_plugins.md](09_sistema_de_plugins.md). Use `pitiupi.chat` como modelo. + +## 1. Novo projeto + +- Copie a pasta `pitiupi.chat` para `pitiupi.meuPlugin` (nome à escolha). +- Ajuste `project.properties` / `build.xml` com o nome do JAR de saída. +- Mantenha `javac.classpath` apontando para `../pitiupi/dist/pitiupi.jar`. + +## 2. Classe principal + +```java +public class MeuPlugin extends Plugin { + @Override + public void createPlugin(ConnectionManager cm, GuiManager gui) { + // registrar menus, abrir janelas + } + + @Override + public List> getMessages() { + return List.of(MinhaMessage.class); + } + + @Override + public void receiveMessage(Message message) { + if (message instanceof MinhaMessage m) { /* ... */ } + } + + // getName, getAuthor, getVersion, getIdentifier +} +``` + +## 3. Mensagem + +```java +public class MinhaMessage extends Message implements Serializable { + // campos + serialVersionUID recomendado +} +``` + +Envio típico via `ConnectionManager` (multicast ou unicast UDP, ou TCP conforme o caso). + +## 4. Service Provider Interface + +Arquivo `src/META-INF/services/pitiupi.models.Plugin`: + +``` +pacote.MeuPlugin +``` + +## 5. Build e instalação + +```bash +cd pitiupi.meuPlugin && ant jar +ln -sf ../../pitiupi.meuPlugin/dist/*.jar ../pitiupi/plugins/ +``` + +Reinicie o Pitiupi após trocar JARs. + +## 6. Teste + +Duas instâncias, mesma rede, portas distintas se no mesmo host. Ver [00_quickstart.md](00_quickstart.md). + +## TCP dedicado (avançado) + +Se o plugin precisa de canal TCP com handshake por `getIdentifier()`, estude `pitiupi.mpChat` e os métodos opcionais em `Plugin` (`receivePrivateConnection`, `receiveBytes`). Ver [11_plugins_referencia.md](11_plugins_referencia.md). + +## Erros frequentes + +| Problema | Causa provável | +| --- | --- | +| Plugin não aparece | SPI ausente ou JAR fora de `plugins/` | +| `ClassNotFoundException` na recepção | Classe da mensagem não no JAR do remetente/destino | +| Peers não se veem | Multicast bloqueado — [appendix_portas_e_multicast.md](appendix_portas_e_multicast.md) | diff --git a/docs/documentation/11_plugins_referencia.md b/docs/documentation/11_plugins_referencia.md new file mode 100644 index 0000000..0f8360e --- /dev/null +++ b/docs/documentation/11_plugins_referencia.md @@ -0,0 +1,59 @@ +# Plugins de referência + +Três exemplos no repositório para comparar estilos de integração. Todos devem ser empacotados em `pitiupi/plugins/`. + +## Comparativo + +| | pitiupi.chat | pitiupi.screenCast | pitiupi.mpChat | +| --- | --- | --- | --- | +| Build | Ant | Ant | Make | +| Classe | `ChatPlugin` | `ScreenCastPlugin` | `MultiProtocolChat` | +| Categoria menu | `Social` | (menu custom via `GuiManager`) | `Social` (multicast + unicast) | +| Mensagem | `ChatMessage` | `ScreenCastMessage` | `MpChatMessage` | +| Transporte principal | UDP (via `ConnectionManager`) | UDP / payloads maiores | UDP público + **TCP privado** | +| TCP avançado | Não | Depende da versão do módulo | `receivePrivateConnection`, `receiveBytes` | + +## pitiupi.chat + +- **Referência canônica** alinhada ao contrato atual `Plugin` + `ConnectionManager` / `GuiManager`. +- `createPlugin` registra `ChatMenu` em `MenuCategory.Social`. +- `getMessages()` retorna `ChatMessage.class`; `receiveMessage` atualiza `ChatWindow`. + +Caminhos: `pitiupi.chat/src/pitiupi/chat/plugin/ChatPlugin.java`. + +## pitiupi.screenCast + +- Demonstra compartilhamento de tela / imagens (`ScreenCastWindow`, `ScreenCastMessage`). +- Útil para discutir **tamanho de datagramas**, compressão (`ImageTransformation`) e limites UDP. + +Caminhos: `pitiupi.screenCast/src/pitiupi/screenCast/`. Alinhado ao contrato `Plugin` + `ConnectionManager` / `GuiManager` (como `ChatPlugin`). + +## pitiupi.mpChat + +- Chat **público** (multicast / unicast UDP) e **privado** (TCP). +- `createPlugin(ConnectionManager, GuiManager)` como nos demais plugins; usa `ConnectionManager.connectPluginTcp` para abrir canal privado. +- Sobrescreve hooks TCP do `Plugin` (`receivePrivateConnection`, `receiveBytes`, `onSocketClosed`). + +Caminhos: `pitiupi.mpChat/src/plugin/MultiProtocolChat.java`. + +## Fluxo UDP (todos os plugins) + +```mermaid +sequenceDiagram + participant App as MainWindow + participant UDP as SocketUDP + participant HB as HeartbeatManager + participant PM as PluginManager + participant Peer as OutroPeer + App->>UDP: join multicast + UDP->>HB: HeartbeatMessage + Peer->>UDP: Message plugin + UDP->>PM: handleMessage bytes + PM->>PM: deserialize + dispatch +``` + +Heartbeat não passa pelos plugins — ver [08_heartbeat_e_presenca.md](08_heartbeat_e_presenca.md). + +## Próximo passo + +Criar plugin próprio: [10_tutorial_plugin.md](10_tutorial_plugin.md). diff --git a/docs/documentation/12_menus_unicast_multicast.md b/docs/documentation/12_menus_unicast_multicast.md new file mode 100644 index 0000000..f2321c3 --- /dev/null +++ b/docs/documentation/12_menus_unicast_multicast.md @@ -0,0 +1,35 @@ +# Menus — unicast e multicast + +## MenuCategory + +Enum em `pitiupi.models.Menus.MenuCategory`: + +| Valor | Uso sugerido | +| --- | --- | +| `Jogos` | Plugins de jogos entre peers | +| `Social` | Chat, presença, mensagens sociais | +| `Streaming` | Screen cast, mídia | + +## Registrar menu (padrão atual) + +Via `GuiManager.createMenu(MenuCategory category, Menu menu)` — ver `ChatPlugin`: + +```java +guiManager.createMenu(MenuCategory.Social, new ChatMenu(chatWindow)); +``` + +A hierarquia de menus unicast/multicast do núcleo (`UnicastMenu`, `MulticastMenu`, etc.) permite ações por peer ou broadcast; plugins de referência encapsulam isso em classes `*Menu` próprias. + +## Unicast vs multicast na aplicação + +| Modo | API típica | Semântica | +| --- | --- | --- | +| Multicast | `ConnectionManager` → `SocketUDP.sendMulticast` | Todos no grupo na porta configurada | +| Unicast UDP | envio para IP de um peer | Um destinatário, sem estabelecer TCP | +| TCP | `ConnectionManager` / `SocketTCP` | Canal persistente, framing 4+N bytes | + +Detalhes de rede: [05_udp_multicast.md](05_udp_multicast.md), [06_tcp_conexoes.md](06_tcp_conexoes.md). + +## mpChat + +`MulticastMpChatMenu` e `UnicastMpChatMenu` mostram como expor as duas modalidades no mesmo plugin — [11_plugins_referencia.md](11_plugins_referencia.md). diff --git a/docs/documentation/13_guia_dev_e_agente.md b/docs/documentation/13_guia_dev_e_agente.md new file mode 100644 index 0000000..61c48f1 --- /dev/null +++ b/docs/documentation/13_guia_dev_e_agente.md @@ -0,0 +1,42 @@ +# Guia para desenvolvedor e agente + +## Leitura mínima antes de mudar código + +1. [00_quickstart.md](00_quickstart.md) — build e execução +2. [04_inicializacao_e_gui.md](04_inicializacao_e_gui.md) — boot e diagrama +3. [09_sistema_de_plugins.md](09_sistema_de_plugins.md) — contrato de extensão +4. [14_arquitetura_e_manutencao.md](14_arquitetura_e_manutencao.md) — mapa de pacotes + +## Por tipo de tarefa + +| Tarefa | Ler primeiro | +| --- | --- | +| Novo plugin (aluno) | [10_tutorial_plugin.md](10_tutorial_plugin.md), [11_plugins_referencia.md](11_plugins_referencia.md) | +| Bug UDP / multicast | [05_udp_multicast.md](05_udp_multicast.md), [appendix_portas_e_multicast.md](appendix_portas_e_multicast.md) | +| Bug TCP / conexão | [06_tcp_conexoes.md](06_tcp_conexoes.md) | +| Presença / lista de usuários | [08_heartbeat_e_presenca.md](08_heartbeat_e_presenca.md) | +| Desserialização / mensagens | [07_mensagens_e_serializacao.md](07_mensagens_e_serializacao.md) | + +## Gate de build + +Compilar núcleo e os três plugins de referência: + +```bash +cd pitiupi && ant jar +cd ../pitiupi.chat && ant jar +cd ../pitiupi.screenCast && ant jar +cd ../pitiupi.mpChat && make +``` + +Requer JDK, Ant e Make no PATH. Teste manual em rede: [15_testes_e_gate.md](15_testes_e_gate.md). + +## Convenções + +- Código e identificadores em inglês (pacotes `pitiupi.*`). +- Documentação em português (BR) em `docs/`. +- Evitar alterar o núcleo para necessidades de um único plugin — preferir extensão via `Plugin`. + +## Decisões e débitos + +- [decision-register.md](../decision-register.md) +- [engineering_backlog.md](engineering_backlog.md) diff --git a/docs/documentation/14_arquitetura_e_manutencao.md b/docs/documentation/14_arquitetura_e_manutencao.md new file mode 100644 index 0000000..1a3bf59 --- /dev/null +++ b/docs/documentation/14_arquitetura_e_manutencao.md @@ -0,0 +1,42 @@ +# Arquitetura e manutenção + +## Onde editar + +| Comportamento | Pacote / classe | +| --- | --- | +| Login, porta, nome | `pitiupi.GUI.Login` | +| Orquestração GUI + plugins | `pitiupi.GUI.MainWindow` | +| Barra de menus base | `pitiupi.GUI.MenuBar` | +| Usuários online | `pitiupi.GUI.OnlineUsersPanel`, `HeartbeatManager` | +| Facade rede para plugins | `pitiupi.control.ConnectionManager` | +| Registro de menus | `pitiupi.control.GuiManager` | +| UDP | `pitiupi.net.udp.SocketUDP` | +| TCP servidor e mapa de conexões | `pitiupi.net.tcp.SocketTCP`, `TcpConnectionManager` | +| Conexão TCP por peer | `pitiupi.net.tcp.TcpConnection` | +| Handshake / fábrica TCP | `pitiupi.net.tcp.ConnectionFactory`, `HandshakeMessage` | +| Heartbeat | `pitiupi.control.HeartbeatManager`, `HeartbeatMessage` | +| Carregar JARs, despacho | `pitiupi.control.plugins.PluginManager` | +| Contrato plugin | `pitiupi.models.Plugin`, `pitiupi.models.Messages.Message` | +| Menus por categoria | `pitiupi.models.Menus.*` | + +## Dependências entre módulos + +``` +pitiupi (núcleo) + ↑ classpath +pitiupi.chat | pitiupi.screenCast | pitiupi.mpChat +``` + +Plugins **não** devem ser dependência do núcleo. + +## Alterações arriscadas + +- Mudar grupo multicast ou formato de heartbeat → quebra interoperabilidade na sala. +- Mudar framing TCP → todos os plugins que usam TCP precisam acompanhar. +- Alterar nome do arquivo SPI (`pitiupi.models.Plugin`) → todos os JARs de plugin. + +Registrar decisão em [decision-register.md](../decision-register.md). + +## Documentação viva + +Ao mudar protocolo ou API pública do núcleo, atualizar o número correspondente em [documentation/README.md](README.md) e, se aplicável, [11_plugins_referencia.md](11_plugins_referencia.md). diff --git a/docs/documentation/15_testes_e_gate.md b/docs/documentation/15_testes_e_gate.md new file mode 100644 index 0000000..ab1bc64 --- /dev/null +++ b/docs/documentation/15_testes_e_gate.md @@ -0,0 +1,38 @@ +# Testes e gate + +## Estado atual + +Não há suíte JUnit integrada ao repositório. A validação principal é: + +1. **Build** de todos os módulos. +2. **Teste manual** com duas instâncias na rede. + +## Gate de build (recomendado) + +Sequência: + +```bash +cd pitiupi && ant jar +cd ../pitiupi.chat && ant jar +cd ../pitiupi.screenCast && ant jar +cd ../pitiupi.mpChat && make +``` + +Falha se `java`, **`javac`**, Ant ou Make estiverem ausentes (instale `openjdk-*-jdk`, não só JRE). + +## Checklist manual (rede) + +- [ ] Duas instâncias com portas distintas (mesmo host) ou dois hosts na mesma LAN +- [ ] Nomes visíveis no painel de usuários em até ~15 s +- [ ] Plugin de referência troca mensagens entre os dois +- [ ] Encerrar uma instância remove o peer da lista após timeout de heartbeat + +## Checklist plugin de aluno + +- [ ] JAR em `plugins/` + SPI correto +- [ ] README do módulo com build e formato das mensagens +- [ ] Demonstração mínima professor ↔ aluno ou aluno ↔ aluno + +## Melhorias futuras + +Ver [engineering_backlog.md](engineering_backlog.md) (testes automatizados, CI). diff --git a/docs/documentation/16_fundamentacao_redes.md b/docs/documentation/16_fundamentacao_redes.md new file mode 100644 index 0000000..8f135a2 --- /dev/null +++ b/docs/documentation/16_fundamentacao_redes.md @@ -0,0 +1,52 @@ +# Fundamentação — redes e código + +Mapa entre conceitos de **Redes de Computadores** e o Pitiupi. Aprofundamento: [references/README.md](../references/README.md). + +## Camada de aplicação P2P + +| Conceito | No Pitiupi | +| --- | --- | +| Aplicação distribuída sem servidor dedicado | Cada peer executa o mesmo núcleo + plugins | +| Identidade do participante | Nome no login + `HeartbeatMessage` (instância) | +| Broadcast lógico | UDP multicast no grupo `224.0.0.3` | +| Comunicação 1:1 | UDP unicast ou TCP com conexão persistente | + +## UDP + +- Sem conexão; mensagens delimitadas por datagrama. +- Multicast economiza envios para todos os peers do grupo. +- Limitações: perda, ordem, MTU — relevante para plugins com imagens (screenCast). + +Doc: [05_udp_multicast.md](05_udp_multicast.md). + +## TCP + +- Conexão orientada a fluxo; o núcleo adiciona **framing** (tamanho + payload). +- Uma conexão por endereço de peer reutilizada para várias mensagens. +- Fechamento quando peer some do heartbeat. + +Doc: [06_tcp_conexoes.md](06_tcp_conexoes.md). + +## Presença + +Heartbeats periódicos em UDP; timeout ~15 s. Analogia com “keepalive” de presença, não com camada de transporte confiável. + +Doc: [08_heartbeat_e_presenca.md](08_heartbeat_e_presenca.md). + +## Serialização + +Objetos Java `Serializable` em `byte[]` — acoplamento de versão de classes entre peers (mesmo plugin instalado). + +Doc: [07_mensagens_e_serializacao.md](07_mensagens_e_serializacao.md). + +## Extensibilidade + +SPI e classloader por JAR — padrão de plug-in em aplicações Java. + +Doc: [09_sistema_de_plugins.md](09_sistema_de_plugins.md). + +## Leitura complementar + +- [references/notas/udp_vs_tcp.md](../references/notas/udp_vs_tcp.md) +- [references/notas/multicast_lan.md](../references/notas/multicast_lan.md) +- [references/notas/java_spi_plugins.md](../references/notas/java_spi_plugins.md) diff --git a/docs/documentation/README.md b/docs/documentation/README.md new file mode 100644 index 0000000..75751b2 --- /dev/null +++ b/docs/documentation/README.md @@ -0,0 +1,27 @@ +# Documentação técnica — Pitiupi + +Referência para **código Java**, **rede P2P** e **plugins**. Índice geral: [docs/README.md](../README.md). + +## Ordem sugerida + +| # | Arquivo | Conteúdo | +| --- | --- | --- | +| 0 | [00_quickstart.md](00_quickstart.md) | Build, plugins, duas instâncias | +| 1 | [01_visao_disciplina_e_tp.md](01_visao_disciplina_e_tp.md) | Uso na disciplina e trabalho prático | +| 2 | [02_modulos_do_repositorio.md](02_modulos_do_repositorio.md) | Núcleo e três plugins de referência | +| 3 | [03_build_ant_netbeans.md](03_build_ant_netbeans.md) | Ant, NetBeans, Makefile mpChat | +| 4 | [04_inicializacao_e_gui.md](04_inicializacao_e_gui.md) | Boot, MainWindow, diagrama | +| 5 | [05_udp_multicast.md](05_udp_multicast.md) | SocketUDP | +| 6 | [06_tcp_conexoes.md](06_tcp_conexoes.md) | SocketTCP, TcpConnection, framing | +| 7 | [07_mensagens_e_serializacao.md](07_mensagens_e_serializacao.md) | Classe Message | +| 8 | [08_heartbeat_e_presenca.md](08_heartbeat_e_presenca.md) | HeartbeatManager | +| 9 | [09_sistema_de_plugins.md](09_sistema_de_plugins.md) | Plugin, PluginManager, SPI | +| 10 | [10_tutorial_plugin.md](10_tutorial_plugin.md) | Passo a passo novo plugin | +| 11 | [11_plugins_referencia.md](11_plugins_referencia.md) | chat, screenCast, mpChat | +| 12 | [12_menus_unicast_multicast.md](12_menus_unicast_multicast.md) | MenuCategory e ações | +| 13 | [13_guia_dev_e_agente.md](13_guia_dev_e_agente.md) | Leitura para dev/agente | +| 14 | [14_arquitetura_e_manutencao.md](14_arquitetura_e_manutencao.md) | Onde editar cada comportamento | +| 15 | [15_testes_e_gate.md](15_testes_e_gate.md) | Build gate e testes manuais | +| 16 | [16_fundamentacao_redes.md](16_fundamentacao_redes.md) | Teoria ↔ código | +| — | [appendix_portas_e_multicast.md](appendix_portas_e_multicast.md) | Portas, multicast, WSL | +| — | [engineering_backlog.md](engineering_backlog.md) | Débitos técnicos | diff --git a/docs/documentation/appendix_portas_e_multicast.md b/docs/documentation/appendix_portas_e_multicast.md new file mode 100644 index 0000000..d6d6597 --- /dev/null +++ b/docs/documentation/appendix_portas_e_multicast.md @@ -0,0 +1,38 @@ +# Apêndice — portas e multicast + +## Configuração + +- **Nome** e **porta** definidos no diálogo de login (`Login`). +- A mesma porta é usada para UDP (multicast socket) e TCP (`ServerSocket`). +- Preferências persistidas localmente (`java.util.prefs`). + +## Multicast + +- Grupo: **`224.0.0.3`** (fixo no código do núcleo). +- Buffer UDP: **256000** bytes (configuração em `SocketUDP`). + +Todos os peers na mesma LAN devem usar a **mesma porta** para participar do mesmo “grupo lógico” da aula (portas diferentes = grupos isolados). + +## Mesmo computador + +Execute duas instâncias com **portas diferentes** (ex.: 5000 e 5001). Mesmo nome de usuário em duas instâncias pode confundir a lista — use nomes distintos. + +## Firewall e Wi‑Fi + +- Bloqueio de multicast em redes de visitantes ou isolamento AP→AP impede descoberta. +- Em laboratório, preferir cabo ou Wi‑Fi com multicast habilitado. + +## WSL2 + +Executar o Pitiupi no WSL e peers no Windows (ou vice-versa) pode falhar em multicast. Preferir: + +- todos os peers no mesmo ambiente (Linux nativo ou VMs), ou +- dois PCs físicos na mesma sub-rede. + +## Sintomas + +| Sintoma | Verificar | +| --- | --- | +| Lista de usuários vazia | Porta igual entre peers? Mesma sub-rede? Firewall? | +| Plugin não recebe | Desserialização / SPI / JAR em `plugins/` | +| TCP falha | Peer ativo no heartbeat? IP correto? | diff --git a/docs/documentation/engineering_backlog.md b/docs/documentation/engineering_backlog.md new file mode 100644 index 0000000..7317edd --- /dev/null +++ b/docs/documentation/engineering_backlog.md @@ -0,0 +1,11 @@ +# Engineering backlog + +| Item | Prioridade | Notas | +| --- | --- | --- | +| Completar tutorial legado em `pitiupi/docs/PluginTutorial.md` | Média | Substituído por [10_tutorial_plugin.md](10_tutorial_plugin.md) | +| Testes JUnit para desserialização e framing TCP | Média | Sem rede real | +| CI (GitHub Actions) com `ant jar` + `make` | Baixa | Requer imagem com Ant | + +Itens concluídos recentemente: alinhamento de `pitiupi.mpChat` a `ConnectionManager` / `GuiManager`; documentação de JDK mínimo no [README](../../README.md) e [00_quickstart.md](00_quickstart.md). + +Ao fechar um item, atualizar esta tabela e o doc técnico relacionado. diff --git a/docs/references/README.md b/docs/references/README.md new file mode 100644 index 0000000..a290988 --- /dev/null +++ b/docs/references/README.md @@ -0,0 +1,18 @@ +# Referências — Pitiupi + +Bibliografia e notas curtas para apoiar a disciplina de Redes de Computadores. + +| Recurso | Descrição | +| --- | --- | +| [bibliografia.bib](bibliografia.bib) | Entradas BibTeX (livro-texto, RFCs, documentação Java) | +| [notas/](notas/) | Resumos em PT-BR ligados ao código | +| [pdfs/](pdfs/) | PDFs locais (não versionados se grandes; ver `.gitkeep`) | + +## Notas disponíveis + +- [udp_vs_tcp.md](notas/udp_vs_tcp.md) +- [multicast_lan.md](notas/multicast_lan.md) +- [java_serializacao.md](notas/java_serializacao.md) +- [java_spi_plugins.md](notas/java_spi_plugins.md) + +Voltar ao índice: [docs/README.md](../README.md). diff --git a/docs/references/bibliografia.bib b/docs/references/bibliografia.bib new file mode 100644 index 0000000..f0a390e --- /dev/null +++ b/docs/references/bibliografia.bib @@ -0,0 +1,24 @@ +@book{tanenbaum2011, + title = {Computer Networks}, + author = {Tanenbaum, Andrew S. and Wetherall, David J.}, + edition = {5}, + year = {2011}, + publisher = {Prentice Hall}, + note = {UDP, TCP, multicast — conceitos gerais} +} + +@misc{rfc1112, + title = {Host Extensions for IP Multicasting}, + author = {Deering, S.}, + howpublished = {RFC 1112}, + year = {1989}, + url = {https://www.rfc-editor.org/rfc/rfc1112} +} + +@book{oracle_networking, + title = {Java Platform, Standard Edition Documentation: Networking}, + author = {{Oracle}}, + year = {2024}, + url = {https://docs.oracle.com/en/java/javase/}, + note = {MulticastSocket, Socket, serialization} +} diff --git a/docs/references/notas/java_serializacao.md b/docs/references/notas/java_serializacao.md new file mode 100644 index 0000000..d269334 --- /dev/null +++ b/docs/references/notas/java_serializacao.md @@ -0,0 +1,11 @@ +# Serialização Java (nota) + +O Pitiupi serializa objetos `Message` (Java `Serializable`) para `byte[]` antes do envio. + +Implicações: + +- Mesma versão de classe nos peers (mesmo JAR de plugin). +- `serialVersionUID` explícito evita incompatibilidade silenciosa. +- Desserialização no núcleo usa `PluginObjectInputStream` com classloader do plugin. + +Doc: [07_mensagens_e_serializacao.md](../../documentation/07_mensagens_e_serializacao.md). diff --git a/docs/references/notas/java_spi_plugins.md b/docs/references/notas/java_spi_plugins.md new file mode 100644 index 0000000..fa9198f --- /dev/null +++ b/docs/references/notas/java_spi_plugins.md @@ -0,0 +1,9 @@ +# Java SPI — plugins (nota) + +O núcleo usa `ServiceLoader` para localizar implementações de `pitiupi.models.Plugin` declaradas em: + +`META-INF/services/pitiupi.models.Plugin` + +Uma linha por classe concreta (FQCN). O JAR deve estar em `pitiupi/plugins/`. + +Doc: [09_sistema_de_plugins.md](../../documentation/09_sistema_de_plugins.md). diff --git a/docs/references/notas/multicast_lan.md b/docs/references/notas/multicast_lan.md new file mode 100644 index 0000000..140d03c --- /dev/null +++ b/docs/references/notas/multicast_lan.md @@ -0,0 +1,12 @@ +# Multicast em LAN (nota) + +IP multicast envia um datagrama que roteadores/switches encaminham aos assinantes do grupo (ex.: `224.0.0.3`). + +Na sala de aula: + +- Todos os peers devem estar na mesma sub-rede e permitir IGMP/multicast. +- Wi‑Fi com “client isolation” impede descoberta. + +Operação no código: [appendix_portas_e_multicast.md](../../documentation/appendix_portas_e_multicast.md). + +RFC introdutória: ver entrada `rfc1112` em [bibliografia.bib](../bibliografia.bib). diff --git a/docs/references/notas/udp_vs_tcp.md b/docs/references/notas/udp_vs_tcp.md new file mode 100644 index 0000000..e47607e --- /dev/null +++ b/docs/references/notas/udp_vs_tcp.md @@ -0,0 +1,10 @@ +# UDP vs TCP (nota) + +| | UDP | TCP | +| --- | --- | --- | +| Conexão | Não | Sim | +| Confiabilidade | Melhor esforço | Entrega ordenada na conexão | +| Delimitação de mensagem | Por datagrama | Fluxo de bytes — Pitiupi usa framing 4+N | +| Uso no Pitiupi | Heartbeat, multicast, plugins | Canais diretos peer-a-peer | + +Ver [05_udp_multicast.md](../../documentation/05_udp_multicast.md) e [06_tcp_conexoes.md](../../documentation/06_tcp_conexoes.md). diff --git a/docs/references/pdfs/.gitkeep b/docs/references/pdfs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/pitiupi.chat/build/built-jar.properties b/pitiupi.chat/build/built-jar.properties deleted file mode 100644 index 54fc683..0000000 --- a/pitiupi.chat/build/built-jar.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri, 02 Oct 2026 20:11:21 -0300 - - -C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi.chat= - -C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi= diff --git a/pitiupi.chat/build/classes/META-INF/services/pitiupi.plugin.Plugin b/pitiupi.chat/build/classes/META-INF/services/pitiupi.plugin.Plugin deleted file mode 100644 index eea07c1..0000000 --- a/pitiupi.chat/build/classes/META-INF/services/pitiupi.plugin.Plugin +++ /dev/null @@ -1 +0,0 @@ -pitiupi.chat.plugin.ChatPlugin diff --git a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatMenu.class b/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatMenu.class deleted file mode 100644 index af3942e..0000000 Binary files a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatMenu.class and /dev/null differ diff --git a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow$1.class b/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow$1.class deleted file mode 100644 index 3d3023d..0000000 Binary files a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow$1.class and /dev/null differ diff --git a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow.class b/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow.class deleted file mode 100644 index 34763e9..0000000 Binary files a/pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow.class and /dev/null differ diff --git a/pitiupi.chat/build/classes/pitiupi/chat/net/ChatMessage.class b/pitiupi.chat/build/classes/pitiupi/chat/net/ChatMessage.class deleted file mode 100644 index 661793b..0000000 Binary files a/pitiupi.chat/build/classes/pitiupi/chat/net/ChatMessage.class and /dev/null differ diff --git a/pitiupi.chat/build/classes/pitiupi/chat/plugin/ChatPlugin.class b/pitiupi.chat/build/classes/pitiupi/chat/plugin/ChatPlugin.class deleted file mode 100644 index dca047b..0000000 Binary files a/pitiupi.chat/build/classes/pitiupi/chat/plugin/ChatPlugin.class and /dev/null differ diff --git a/pitiupi.mpChat/src/GUI/PrivateChatWindow.java b/pitiupi.mpChat/src/GUI/PrivateChatWindow.java index ff4e3d3..0ec9968 100644 --- a/pitiupi.mpChat/src/GUI/PrivateChatWindow.java +++ b/pitiupi.mpChat/src/GUI/PrivateChatWindow.java @@ -1,8 +1,8 @@ package GUI; import net.MpChatMessage; +import pitiupi.control.ConnectionManager; import pitiupi.net.tcp.TcpConnection; -import pitiupi.plugin.Pitiupi; import javax.swing.*; import java.awt.*; @@ -12,17 +12,17 @@ import java.util.logging.Logger; public class PrivateChatWindow extends JFrame { - private final Pitiupi pitiupi; + private final ConnectionManager connectionManager; private final TcpConnection connection; private final JTextArea receivedMessagesArea; private final JTextField messageField; public PrivateChatWindow( - Pitiupi pitiupi, + ConnectionManager connectionManager, TcpConnection connection ) { - this.pitiupi = pitiupi; + this.connectionManager = connectionManager; this.connection = connection; setTitle("Chat privado"); @@ -81,7 +81,7 @@ public class PrivateChatWindow extends JFrame { private void sendTCP() { try { - String nome = pitiupi.getUserName(); + String nome = connectionManager.getUserName(); MpChatMessage message = new MpChatMessage( nome, diff --git a/pitiupi.mpChat/src/GUI/PublicChatWindow.java b/pitiupi.mpChat/src/GUI/PublicChatWindow.java index 67cbafa..3716217 100644 --- a/pitiupi.mpChat/src/GUI/PublicChatWindow.java +++ b/pitiupi.mpChat/src/GUI/PublicChatWindow.java @@ -2,7 +2,6 @@ package GUI; import net.MpChatMessage; import pitiupi.control.ConnectionManager; -import pitiupi.plugin.Pitiupi; import javax.swing.*; import javax.swing.text.BadLocationException; @@ -15,14 +14,14 @@ import java.util.logging.Level; import java.util.logging.Logger; public class PublicChatWindow extends JFrame { - private final Pitiupi pitiupi; + private final ConnectionManager connectionManager; private JTextPane receivedMessagesArea; private JTextField messageField; private JTextField ipField; - public PublicChatWindow(Pitiupi pitiupi) { - this.pitiupi = pitiupi; + public PublicChatWindow(ConnectionManager connectionManager) { + this.connectionManager = connectionManager; initComponents(); } @@ -120,7 +119,7 @@ public class PublicChatWindow extends JFrame { private MpChatMessage createMessage(String tipoMensagem) { try { - String nome = pitiupi.getUserName(); + String nome = connectionManager.getUserName(); String ip = InetAddress.getLocalHost().getHostAddress(); String message = messageField.getText(); @@ -177,7 +176,7 @@ public class PublicChatWindow extends JFrame { InetAddress ip = InetAddress.getByName(ipField.getText()); - pitiupi.sendTcp(msg, ip); + connectionManager.sendTcp(msg, ip); receiveMessage(msg); } catch (Exception ex) { @@ -193,7 +192,7 @@ public class PublicChatWindow extends JFrame { InetAddress ip = InetAddress.getByName(ipField.getText()); - pitiupi.sendUdpUnicast(msg, ip); + connectionManager.sendUdpUnicast(msg, ip); receiveMessage(msg); } catch (Exception ex) { @@ -207,7 +206,7 @@ public class PublicChatWindow extends JFrame { try { MpChatMessage msg = createMessage("UDP Multicast"); - pitiupi.sendUdpMulticast(msg); + connectionManager.sendUdpMulticast(msg); } catch (Exception ex) { Logger.getLogger( diff --git a/pitiupi.mpChat/src/control/ChatController.java b/pitiupi.mpChat/src/control/ChatController.java deleted file mode 100644 index 07f7bb7..0000000 --- a/pitiupi.mpChat/src/control/ChatController.java +++ /dev/null @@ -1,73 +0,0 @@ -package control; - -import GUI.ChatMenu; -import GUI.ChatWindow; -import GUI.PrivateChatWindow; -import GUI.PublicChatWindow; -import net.MpChatMessage; -import pitiupi.control.ConnectionManager; -import pitiupi.control.PeerListener; -import pitiupi.models.User; -import pitiupi.net.tcp.TcpConnection; - -import javax.swing.*; -import java.net.InetAddress; -import java.util.HashMap; -import java.util.List; - -public class ChatController implements PeerListener { - ConnectionManager connectionManager; - ChatMenu chatMenu; - - PublicChatWindow publicChatWindow; - - private final HashMap chatWindows; - - public ChatController(ConnectionManager connectionManager, ChatMenu chatMenu) { - this.connectionManager = connectionManager; - publicChatWindow = new PublicChatWindow(connectionManager); - - chatMenus = new HashMap<>(); - chatWindows = new HashMap<>(); - - this.chatMenu = chatMenu; - - this.chatMenu.newMenuItem(new JMenuItem("Show global chat"), publicChatWindow); - } - - public void addChatWindow(TcpConnection tcpConnection) { - if (chatWindows.containsKey(tcpConnection)) return; - - PrivateChatWindow newChat = new PrivateChatWindow(connectionManager, tcpConnection); - chatWindows.put(tcpConnection, newChat); - JMenuItem newMenuItem = new JMenuItem(); - chatMenus.put(tcpConnection, newMenuItem); - newMenuItem.setText(tcpConnection.getAddress().toString()); - chatMenu.newMenuItem(newMenuItem, newChat); - } - - public void removeChatWindow(TcpConnection tcpConnection) { - ChatWindow removedWindow = chatWindows.remove(tcpConnection); - JMenuItem removedMenu = chatMenus.remove(tcpConnection); - - chatMenu.removeMenuItem(removedMenu); - removedWindow.setVisible(false); - removedWindow.dispose(); - } - - public void receivePublic(MpChatMessage message) { - publicChatWindow.receiveMessage(message); - } - public void receivePrivate(TcpConnection tcpConnection, MpChatMessage message) { - chatWindows.get(tcpConnection).receiveMessage(message); - } - - public void openChatWindow(InetAddress address) { - - } - - @Override - public void onPeersChanged(List list) { - - } -} diff --git a/pitiupi.mpChat/src/control/PrivateChatController.java b/pitiupi.mpChat/src/control/PrivateChatController.java index aad138a..d806153 100644 --- a/pitiupi.mpChat/src/control/PrivateChatController.java +++ b/pitiupi.mpChat/src/control/PrivateChatController.java @@ -2,9 +2,9 @@ package control; import GUI.PrivateChatWindow; import net.MpChatMessage; +import pitiupi.control.ConnectionManager; import pitiupi.models.User; import pitiupi.net.tcp.TcpConnection; -import pitiupi.plugin.Pitiupi; import plugin.MultiProtocolChat; import java.util.HashMap; @@ -14,7 +14,7 @@ import java.util.Set; public class PrivateChatController { - private final Pitiupi pitiupi; + private final ConnectionManager connectionManager; private final MultiProtocolChat multiProtocolChat; /* @@ -29,16 +29,20 @@ public class PrivateChatController { private final Set pendingConnections; public PrivateChatController( - Pitiupi pitiupi, + ConnectionManager connectionManager, MultiProtocolChat multiProtocolChat ) { - this.pitiupi = pitiupi; + this.connectionManager = connectionManager; this.multiProtocolChat = multiProtocolChat; this.windows = new HashMap<>(); this.pendingConnections = new HashSet<>(); } + private static String addressKey(TcpConnection connection) { + return connection.getAddress().getHostAddress(); + } + public void openChat(User user) { String address = user.getAddress().getHostAddress(); @@ -70,7 +74,7 @@ public class PrivateChatController { } public void receiveMessage(TcpConnection connection, MpChatMessage message) { - String address = connection.getAddress().toString(); + String address = addressKey(connection); PrivateChatWindow window = windows.get(address); @@ -85,12 +89,12 @@ public class PrivateChatController { * Deve ser chamado quando uma nova conexão privada chegar. */ public void connectionReceived(TcpConnection connection) { - String address = connection.getAddress().toString(); + String address = addressKey(connection); pendingConnections.remove(address); PrivateChatWindow window = new PrivateChatWindow( - pitiupi, + connectionManager, connection ); @@ -105,7 +109,7 @@ public class PrivateChatController { * Fecha a janela e remove o relacionamento. */ public void closeChat(TcpConnection connection) { - String address = connection.getAddress().toString(); + String address = addressKey(connection); PrivateChatWindow window = windows.remove(address); @@ -113,4 +117,4 @@ public class PrivateChatController { window.dispose(); } } -} \ No newline at end of file +} diff --git a/pitiupi.mpChat/src/plugin/MultiProtocolChat.java b/pitiupi.mpChat/src/plugin/MultiProtocolChat.java index bab8d2a..c9915b2 100644 --- a/pitiupi.mpChat/src/plugin/MultiProtocolChat.java +++ b/pitiupi.mpChat/src/plugin/MultiProtocolChat.java @@ -1,16 +1,16 @@ package plugin; import GUI.MulticastMpChatMenu; -import GUI.PrivateChatWindow; import GUI.PublicChatWindow; import GUI.UnicastMpChatMenu; import control.PrivateChatController; import net.MpChatMessage; +import pitiupi.control.ConnectionManager; +import pitiupi.control.GuiManager; import pitiupi.models.Menus.MenuCategory; import pitiupi.models.Messages.Message; -import pitiupi.net.tcp.TcpConnection; import pitiupi.models.Plugin; -import pitiupi.plugin.Pitiupi; +import pitiupi.net.tcp.TcpConnection; import javax.swing.*; import java.io.ByteArrayInputStream; @@ -20,7 +20,7 @@ import java.net.InetAddress; import java.util.List; public class MultiProtocolChat extends Plugin { - Pitiupi pitiupi; + private ConnectionManager connectionManager; PrivateChatController privateChatController; PublicChatWindow publicChatWindow; @@ -46,18 +46,18 @@ public class MultiProtocolChat extends Plugin { } @Override - public void createPlugin(Pitiupi pitiupi) { - this.pitiupi = pitiupi; + public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) { + this.connectionManager = connectionManager; - privateChatController = new PrivateChatController(pitiupi, this); + privateChatController = new PrivateChatController(connectionManager, this); - publicChatWindow = new PublicChatWindow(pitiupi); + publicChatWindow = new PublicChatWindow(connectionManager); MulticastMpChatMenu multicastMpChatMenu = new MulticastMpChatMenu(publicChatWindow); UnicastMpChatMenu unicastMpChatMenu = new UnicastMpChatMenu(privateChatController); - pitiupi.createMenu(MenuCategory.Social, multicastMpChatMenu); - pitiupi.createMenu(MenuCategory.Social, unicastMpChatMenu); + guiManager.createMenu(MenuCategory.Social, multicastMpChatMenu); + guiManager.createMenu(MenuCategory.Social, unicastMpChatMenu); } @Override @@ -102,7 +102,11 @@ public class MultiProtocolChat extends Plugin { public void openConnection(String ip) { try { - pitiupi.connectTcp(InetAddress.getByName(ip), getIdentifier(), getIdentifier()); + connectionManager.connectPluginTcp( + InetAddress.getByName(ip), + getIdentifier(), + getIdentifier() + ); } catch (Exception e) { System.out.println("Error opening connection: " + e.getMessage()); diff --git a/pitiupi.screenCast/build/built-jar.properties b/pitiupi.screenCast/build/built-jar.properties deleted file mode 100644 index 1f21a31..0000000 --- a/pitiupi.screenCast/build/built-jar.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Tue, 29 Sep 2026 11:04:58 -0300 - - -C\:\\Users\\Gustavo\\OneDrive\\Documentos\\Faculdade\\2026_2\\Monitoria\\pitiupi\\pitiupi.screenCast= - -C\:\\Users\\Gustavo\\OneDrive\\Documentos\\Faculdade\\2026_2\\Monitoria\\pitiupi\\pitiupi= diff --git a/pitiupi.screenCast/build/classes/META-INF/services/pitiupi.plugin.Plugin b/pitiupi.screenCast/build/classes/META-INF/services/pitiupi.plugin.Plugin deleted file mode 100644 index d11a8fc..0000000 --- a/pitiupi.screenCast/build/classes/META-INF/services/pitiupi.plugin.Plugin +++ /dev/null @@ -1 +0,0 @@ -pitiupi.screenCast.plugin.ScreenCastPlugin diff --git a/pitiupi.screenCast/build/classes/pitiupi/screenCast/GUI/ScreenCastMenu.class b/pitiupi.screenCast/build/classes/pitiupi/screenCast/GUI/ScreenCastMenu.class deleted file mode 100644 index 4c002f1..0000000 Binary files a/pitiupi.screenCast/build/classes/pitiupi/screenCast/GUI/ScreenCastMenu.class and /dev/null differ diff --git a/pitiupi.screenCast/build/classes/pitiupi/screenCast/GUI/ScreenCastWindow.class b/pitiupi.screenCast/build/classes/pitiupi/screenCast/GUI/ScreenCastWindow.class deleted file mode 100644 index f8d140d..0000000 Binary files a/pitiupi.screenCast/build/classes/pitiupi/screenCast/GUI/ScreenCastWindow.class and /dev/null differ diff --git a/pitiupi.screenCast/build/classes/pitiupi/screenCast/control/AlarmClock.class b/pitiupi.screenCast/build/classes/pitiupi/screenCast/control/AlarmClock.class deleted file mode 100644 index 40e9f43..0000000 Binary files a/pitiupi.screenCast/build/classes/pitiupi/screenCast/control/AlarmClock.class and /dev/null differ diff --git a/pitiupi.screenCast/build/classes/pitiupi/screenCast/control/ImageTransformation.class b/pitiupi.screenCast/build/classes/pitiupi/screenCast/control/ImageTransformation.class deleted file mode 100644 index b4c1d43..0000000 Binary files a/pitiupi.screenCast/build/classes/pitiupi/screenCast/control/ImageTransformation.class and /dev/null differ diff --git a/pitiupi.screenCast/build/classes/pitiupi/screenCast/control/ScreenShot.class b/pitiupi.screenCast/build/classes/pitiupi/screenCast/control/ScreenShot.class deleted file mode 100644 index 3cde756..0000000 Binary files a/pitiupi.screenCast/build/classes/pitiupi/screenCast/control/ScreenShot.class and /dev/null differ diff --git a/pitiupi.screenCast/build/classes/pitiupi/screenCast/net/ScreenCastMessage.class b/pitiupi.screenCast/build/classes/pitiupi/screenCast/net/ScreenCastMessage.class deleted file mode 100644 index dc8a7ea..0000000 Binary files a/pitiupi.screenCast/build/classes/pitiupi/screenCast/net/ScreenCastMessage.class and /dev/null differ diff --git a/pitiupi.screenCast/build/classes/pitiupi/screenCast/plugin/ScreenCastPlugin.class b/pitiupi.screenCast/build/classes/pitiupi/screenCast/plugin/ScreenCastPlugin.class deleted file mode 100644 index b26bee9..0000000 Binary files a/pitiupi.screenCast/build/classes/pitiupi/screenCast/plugin/ScreenCastPlugin.class and /dev/null differ diff --git a/pitiupi.screenCast/dist/README.TXT b/pitiupi.screenCast/dist/README.TXT deleted file mode 100644 index 28b99c7..0000000 --- a/pitiupi.screenCast/dist/README.TXT +++ /dev/null @@ -1,32 +0,0 @@ -======================== -BUILD OUTPUT DESCRIPTION -======================== - -When you build an Java application project that has a main class, the IDE -automatically copies all of the JAR -files on the projects classpath to your projects dist/lib folder. The IDE -also adds each of the JAR files to the Class-Path element in the application -JAR files manifest file (MANIFEST.MF). - -To run the project from the command line, go to the dist folder and -type the following: - -java -jar "pitiupi.screenCast.jar" - -To distribute this project, zip up the dist folder (including the lib folder) -and distribute the ZIP file. - -Notes: - -* If two JAR files on the project classpath have the same name, only the first -JAR file is copied to the lib folder. -* Only JAR files are copied to the lib folder. -If the classpath contains other types of files or folders, these files (folders) -are not copied. -* If a library on the projects classpath also has a Class-Path element -specified in the manifest,the content of the Class-Path element has to be on -the projects runtime path. -* To set a main class in a standard Java project, right-click the project node -in the Projects window and choose Properties. Then click Run and enter the -class name in the Main Class field. Alternatively, you can manually type the -class name in the manifest Main-Class element. diff --git a/pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastMenu.java b/pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastMenu.java index 93abd86..1f87dc5 100644 --- a/pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastMenu.java +++ b/pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastMenu.java @@ -4,28 +4,49 @@ */ package pitiupi.screenCast.GUI; -import java.awt.event.ActionEvent; -import javax.swing.JMenu; +import java.util.List; + +import pitiupi.models.Menus.MulticastMenus.MulticastActionMenu; +import pitiupi.models.Menus.MulticastMenus.MulticastMenu; +import pitiupi.models.Menus.MulticastMenus.MulticastSubMenu; /** * * @author flavio */ -public class ScreenCastMenu extends JMenu { +public class ScreenCastMenu extends MulticastSubMenu { - private final ScreenCastWindow screenCastWindow; - private final javax.swing.JMenuItem menuItem; + private final ScreenCastShowMenu showMenu; public ScreenCastMenu(ScreenCastWindow screenCastWindow) { - this.screenCastWindow = screenCastWindow; - this.setText("Screen Cast"); - menuItem = new javax.swing.JMenuItem(); - menuItem.setText("ScreenCast Window"); - menuItem.addActionListener(this::chatMenuActionPerformed); - this.add(menuItem); + this.showMenu = new ScreenCastShowMenu(screenCastWindow); } - private void chatMenuActionPerformed(ActionEvent ae) { - this.screenCastWindow.setVisible(true); + @Override + public String getText() { + return "Screen Cast"; + } + + @Override + public List getMenus() { + return List.of(showMenu); + } + + private static final class ScreenCastShowMenu extends MulticastActionMenu { + private final ScreenCastWindow screenCastWindow; + + ScreenCastShowMenu(ScreenCastWindow screenCastWindow) { + this.screenCastWindow = screenCastWindow; + } + + @Override + public String getText() { + return "ScreenCast Window"; + } + + @Override + public void onClick() { + screenCastWindow.setVisible(true); + } } } diff --git a/pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastWindow.java b/pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastWindow.java index cfd0022..573dc9b 100644 --- a/pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastWindow.java +++ b/pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastWindow.java @@ -22,8 +22,6 @@ import java.util.logging.Logger; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JLabel; -import pitiupi.GUI.MainWindow; -import pitiupi.net.Message; /** * diff --git a/pitiupi.screenCast/src/pitiupi/screenCast/net/ScreenCastMessage.java b/pitiupi.screenCast/src/pitiupi/screenCast/net/ScreenCastMessage.java index 99b4ae1..d307cf6 100644 --- a/pitiupi.screenCast/src/pitiupi/screenCast/net/ScreenCastMessage.java +++ b/pitiupi.screenCast/src/pitiupi/screenCast/net/ScreenCastMessage.java @@ -5,7 +5,7 @@ */ package pitiupi.screenCast.net; -import pitiupi.net.Message; +import pitiupi.models.Messages.Message; /** * @@ -46,7 +46,7 @@ public class ScreenCastMessage extends Message { } /** - * @param imageByte the image to set + * @param imageByte the imageByte to set */ public void setImageByte(byte[] imageByte) { this.imageByte = imageByte; diff --git a/pitiupi.screenCast/src/pitiupi/screenCast/plugin/ScreenCastPlugin.java b/pitiupi.screenCast/src/pitiupi/screenCast/plugin/ScreenCastPlugin.java index 50641a6..f7dcd55 100644 --- a/pitiupi.screenCast/src/pitiupi/screenCast/plugin/ScreenCastPlugin.java +++ b/pitiupi.screenCast/src/pitiupi/screenCast/plugin/ScreenCastPlugin.java @@ -4,14 +4,13 @@ */ package pitiupi.screenCast.plugin; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import pitiupi.GUI.MainWindow; +import java.util.List; + import pitiupi.control.ConnectionManager; import pitiupi.control.GuiManager; -import pitiupi.net.Message; -import pitiupi.plugin.Plugin; +import pitiupi.models.Menus.MenuCategory; +import pitiupi.models.Messages.Message; +import pitiupi.models.Plugin; import pitiupi.screenCast.GUI.ScreenCastMenu; import pitiupi.screenCast.GUI.ScreenCastWindow; import pitiupi.screenCast.net.ScreenCastMessage; @@ -20,7 +19,7 @@ import pitiupi.screenCast.net.ScreenCastMessage; * * @author flavio */ -public class ScreenCastPlugin implements Plugin { +public class ScreenCastPlugin extends Plugin { private ScreenCastWindow screenCastWindow; @@ -47,26 +46,18 @@ public class ScreenCastPlugin implements Plugin { @Override public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) { this.screenCastWindow = new ScreenCastWindow(connectionManager, guiManager); - guiManager.addMenu(new ScreenCastMenu(screenCastWindow)); + guiManager.createMenu(MenuCategory.Social, new ScreenCastMenu(screenCastWindow)); } @Override - public void receiveMessage(byte[] message) { - ByteArrayInputStream bis = new ByteArrayInputStream(message); - ObjectInputStream ois; - try { - ois = new ObjectInputStream(bis); - if (ois.readObject() instanceof ScreenCastMessage msg) { - this.screenCastWindow.receiveScreenCastMessage(msg); - } - } catch (IOException | ClassNotFoundException ex) { - System.out.println(ex.toString()); + public List> getMessages() { + return List.of(ScreenCastMessage.class); + } + + @Override + public void receiveMessage(Message message) { + if (message instanceof ScreenCastMessage msg) { + this.screenCastWindow.receiveScreenCastMessage(msg); } } - - @Override - public Message getMessage() { - return new ScreenCastMessage(); - } - } diff --git a/pitiupi/docs/Documentation.md b/pitiupi/docs/Documentation.md index 6e15422..1cc1dff 100644 --- a/pitiupi/docs/Documentation.md +++ b/pitiupi/docs/Documentation.md @@ -1,547 +1,5 @@ -# Visão geral +# Documentação (legado) -O `Pitiupi` é uma plataforma P2P baseada em uma arquitetura de plugins, -desenvolvida para permitir a criação de aplicações distribuídas sobre uma -infraestrutura comum de comunicação em rede. +Este arquivo foi substituído pela árvore em **[docs/](../../docs/README.md)**. -A aplicação fornece mecanismos de descoberta de usuários, comunicação entre -peers, troca de mensagens serializadas e integração de extensões por meio de -plugins. A comunicação de rede utiliza dois protocolos de transporte: UDP, -empregado principalmente na descoberta e comunicação multicast entre as -instâncias, e TCP, utilizado para comunicação direta e persistente entre peers. - -Dessa forma, novos comportamentos podem ser adicionados por meio de plugins -sem modificar o núcleo da aplicação. - -O projeto foi desenvolvido como suporte às práticas da disciplina de Redes de -Computadores, servindo também como base para experimentação de aplicações -distribuídas e desenvolvimento de projetos acadêmicos. - -# Arquitetura geral - -A aplicação é dividida em três grupos principais: - -- **Núcleo da aplicação**: A `MainWindow` atua como ponto central da aplicação, - sendo responsável por inicializar e integrar esses componentes durante a - execução. -- **Sistema de plugins**: responsável por adicionar funcionalidades sem - alterar o núcleo da aplicação. -- **Comunicação de rede**: responsável pela troca de mensagens entre - instâncias da aplicação. - -## Inicialização da aplicação - -1. A MainWindow é criada. -2. O SocketUDP é inicializado, associa-se à porta configurada e ingressa no - grupo multicast. -3. O SocketTCP é inicializado e cria um ServerSocket na porta da aplicação - para aceitar conexões TCP. -4. Os mecanismos de comunicação iniciam seus respectivos processos de recepção. -5. O HeartbeatManager é criado e inicia o envio periódico de heartbeats. -6. O painel de usuários online é registrado como listener do HeartbeatManager. -7. O PluginLoader procura e carrega os plugins disponíveis. - -# Sistema de comunicação - -A comunicação entre as instâncias da aplicação é realizada por meio de dois -mecanismos complementares: - -- **UDP multicast/unicast**, implementado por `SocketUDP` -- **TCP**, implementado por `SocketTCP` e `TcpConnection` - -Ambos os mecanismos utilizam mensagens serializadas em `byte[]`, permitindo que -os plugins permaneçam independentes dos detalhes do protocolo de transporte. - -## Comunicação UDP - -A classe `SocketUDP` é responsável pela comunicação UDP da aplicação. - -Ela utiliza um `MulticastSocket` associado à porta configurada pela aplicação e -ingressa no grupo multicast `224.0.0.3`. Por ser baseada em UDP, a comunicação não -estabelece uma conexão permanente entre os peers. - -A classe estende `Thread`, mantendo um processo contínuo de recepção de datagramas -enquanto a aplicação está em execução. - -O SocketUDP suporta dois modos de envio: - -- **multicast**, destinado ao grupo de peers -- **unicast**, destinado diretamente ao endereço IP de um peer específico - -### Inicialização - -Durante sua inicialização, o `SocketUDP`: - -1. obtém o endereço do grupo multicast; -2. cria um `MulticastSocket` utilizando a porta configurada na `MainWindow`; -3. configura os buffers de envio e recepção; -4. habilita o reuso do endereço; -5. ingressa no grupo multicast. - -O tamanho dos buffers de comunicação é configurado para `256000` bytes. - -### Envio de mensagens UDP - -O `SocketUDP` suporta dois modos de envio de mensagens: multicast e unicast, ambos -baseados no mesmo mecanismo interno de criação e envio de DatagramPacket. - -O método `sendMulticast()` envia uma mensagem para o grupo multicast da aplicação. -Nesse caso, o `DatagramPacket` é direcionado ao endereço multicast configurado, -permitindo que todas as instâncias participantes do grupo recebam a mensagem -simultaneamente. - -Já o método `sendUnicast()` permite enviar uma mensagem diretamente para um peer -específico. Nesse caso, o `DatagramPacket` é direcionado ao endereço IP informado pelo -chamador, mantendo a mesma porta utilizada pela aplicação. - -Ambos os métodos são, na prática, abstrações de um único mecanismo interno de envio, -que utiliza o mesmo processo de criação e envio de `DatagramPacket` por baixo dos -panos. A diferença entre eles está apenas no endereço de destino: enquanto o multicast -utiliza o grupo compartilhado, o unicast direciona o pacote a um IP específico. - -### Recepção de mensagens - -O método `receive()` mantém um loop aguardando novos datagramas. - -Ao receber uma mensagem, o `SocketUDP` inicialmente tenta identificar se os dados -correspondem a uma `HeartbeatMessage`. - -O processamento segue o seguinte fluxo: - -1. O datagrama é recebido pelo `MulticastSocket`. -2. Os dados recebidos são analisados pelo método `tryParseHeartbeat()`. -3. Caso seja identificada uma `HeartbeatMessage`, ela é encaminhada ao - `HeartbeatManager`, juntamente com o endereço IP de origem. -4. Caso não seja um heartbeat, a mensagem é encaminhada para todos os plugins - carregados. -5. Cada plugin decide se a mensagem recebida pertence à sua funcionalidade. - -Essa separação mantém as mensagens de infraestrutura, como heartbeat, sob -responsabilidade do núcleo da aplicação, enquanto as mensagens específicas das -funcionalidades são processadas pelos plugins. - -### Identificação de mensagens de heartbeat - -O método `tryParseHeartbeat()` tenta desserializar os dados recebidos e verificar se o -objeto resultante é uma instância de `HeartbeatMessage`. - -Caso a mensagem não possa ser desserializada ou não corresponda ao tipo esperado, o -método retorna `null`. Nesse caso, a mensagem é tratada como uma mensagem destinada aos -plugins. - -### Encerramento - -O método `close()` interrompe a execução do processo de recepção, remove a aplicação -do grupo multicast e fecha o `MulticastSocket`. - -A variável `running` é utilizada para sinalizar que o processo de recepção deve ser -encerrado. - -## Comunicação TCP - -A comunicação TCP é implementada pela classe `SocketTCP`. - -Diferentemente do UDP, o TCP estabelece conexões entre dois peers. Essas conexões -são mantidas enquanto forem necessárias e podem ser reutilizadas para o envio de -múltiplas mensagens. - -A arquitetura utiliza uma conexão TCP persistente por endereço de peer. - -O SocketTCP possui um mapa de conexões: - -`InetAddress` -> `TcpConnection` - -Esse mapa permite localizar rapidamente uma conexão existente para determinado peer e -reutilizá-la durante novos envios. - -### SocketTCP - -A classe `SocketTCP` estende `Thread` e implementa `PeerListener`. - -Ao ser criada, ela inicializa um `ServerSocket` utilizando a porta configurada na -aplicação. - -O servidor TCP permanece aguardando novas conexões enquanto a aplicação estiver -em execução. - -### Envio de mensagens TCP - -O método `send()` recebe uma mensagem serializada e o endereço IP do peer destinatário. - -Antes de enviar a mensagem, o `SocketTCP` verifica se já existe uma conexão válida com -o peer. - -O comportamento é: - -1. Procurar uma conexão existente no mapa de conexões. -2. Verificar se a conexão está fechada. -3. Caso não exista uma conexão válida, criar um novo Socket TCP para o endereço do peer. -4. Encapsular o socket em uma `TcpConnection`. -5. Registrar a conexão no mapa. -6. Iniciar uma tarefa de recepção para essa conexão. -7. Enviar a mensagem pela conexão. - -Assim, uma conexão TCP existente pode ser reutilizada para várias mensagens, evitando -a criação de um novo socket para cada envio. - -### Recepção de conexões - -O método `receive()` permanece aguardando novas conexões através de -`ServerSocket.accept()`. - -Quando uma conexão é aceita: - -O endereço IP do peer é obtido. -Um objeto `TcpConnection` é criado para encapsular o socket. -A conexão é registrada no mapa de conexões. -Caso já exista uma conexão associada ao mesmo endereço, a conexão anterior é encerrada. -Uma tarefa independente de recepção é criada para a nova conexão. - -Esse modelo permite que o servidor aceite múltiplas conexões sem bloquear o -processamento das conexões já existentes. - -### Recepção concorrente - -A recepção das mensagens TCP é realizada de maneira concorrente por meio de um -`ExecutorService`. - -Cada conexão possui uma tarefa própria responsável por receber continuamente as -mensagens. - -O fluxo de recepção é: - -``` -SocketTCP -| -+-- Peer A → TcpConnection → tarefa de recepção -| -+-- Peer B → TcpConnection → tarefa de recepção -| -+-- Peer C → TcpConnection → tarefa de recepção -``` - -Dessa forma, uma conexão bloqueada ou aguardando dados não impede que outras conexões -continuem sendo processadas. - -Quando uma mensagem é recebida por uma `TcpConnection`, ela é encaminhada para todos os -plugins registrados na `MainWindow`. - -O plugin responsável pela funcionalidade deve identificar o tipo da mensagem e -realizar o processamento correspondente. - -### TcpConnection - -A classe TcpConnection encapsula uma conexão TCP individual entre dois peers. - -Ela mantém: - -o Socket utilizado na conexão; -um DataInputStream para recepção; -um DataOutputStream para envio. - -Além de encapsular esses recursos, a classe é responsável pelo framing das mensagens. - -### Framing das mensagens TCP - -A classe `TcpConnection` encapsula uma conexão TCP individual entre dois peers. - -Ela mantém: - -1. o `Socket` utilizado na conexão; -2. um `DataInputStream` para recepção; -3. um `DataOutputStream` para envio. - -Além de encapsular esses recursos, a classe é responsável pelo framing das mensagens. - -Framing das mensagens TCP - -O protocolo TCP fornece um fluxo contínuo de bytes e não preserva os limites das -mensagens enviadas pela aplicação. - -Por esse motivo, é adicionado o tamanho da mensagem antes do conteúdo. - -O formato utilizado é: - -``` -+----------------------+----------------------+ -| tamanho da mensagem | dados da mensagem | -+----------------------+----------------------+ -4 bytes N bytes -``` - -O tamanho é enviado como um `int`, seguido pelos bytes da mensagem. - -No envio, o método `TcpConnection.send()`: - -1. obtém o tamanho do vetor de bytes; -2. escreve o tamanho no `DataOutputStream`; -3. escreve os dados da mensagem; -4. realiza `flush()` no stream. - -O acesso ao stream de saída é sincronizado para evitar que envios concorrentes -misturem seus dados. - -Na recepção, o método `receive()`: - -1. lê o tamanho da mensagem; -2. verifica se o tamanho é válido; -3. lê a quantidade correspondente de bytes; -4. retorna o vetor de bytes completo. - -Esse mecanismo permite que várias mensagens sejam transmitidas pela mesma conexão -TCP sem que o receptor perca a delimitação entre elas. - -### Integração entre TCP e gerenciamento de peers - -O `SocketTCP` implementa a interface PeerListener para acompanhar as alterações na -lista de peers ativos. - -O `HeartbeatManager` mantém a lista de peers atualmente detectados na rede. Quando -essa lista é alterada, o SocketTCP recebe a notificação por meio do método -`onPeersChanged()`. - -O método obtém os endereços IP dos peers atualmente ativos e compara esses endereços -com as conexões TCP existentes. - -Quando uma conexão pertence a um peer que não está mais ativo: - -a conexão é removida do mapa; -a conexão TCP é encerrada; -os recursos associados são liberados. - -Esse mecanismo impede que conexões TCP permaneçam abertas indefinidamente para peers -que já deixaram de participar da rede. - -O fluxo de integração pode ser representado da seguinte forma: -``` -HeartbeatManager -| -| peers ativos alterados -| -v -SocketTCP.onPeersChanged() -| -| compara peers ativos -| -v -+---- peer ativo ------> mantém conexão -| -+---- peer inativo ----> fecha conexão -``` - -### Message - -A classe `Message` representa a estrutura base das mensagens trocadas pela aplicação. - -Todas as mensagens utilizadas pelo sistema devem herdar dessa classe. Ela implementa -`Serializable`, permitindo que objetos de mensagem sejam convertidos em vetores de -bytes para transmissão pela rede. - -A serialização é realizada pelo método `toByteArray()`, que transforma uma instância -da mensagem em uma representação binária enviada pelos mecanismos de comunicação. - -As mensagens específicas devem estender essa classe, adicionando os atributos e -comportamentos necessários para cada funcionalidade. - -A camada de transporte não depende do conteúdo específico das mensagens. Tanto o -UDP quanto o TCP recebem mensagens serializadas como `byte[]`. - -### Heartbeat - -O mecanismo de heartbeat é utilizado para identificar quais usuários estão ativos -na rede. - -A aplicação envia periodicamente uma `HeartbeatMessage` contendo informações do usuário, -um identificador único da instância da aplicação e um timestamp. - -Essas mensagens são transmitidas utilizando a comunicação UDP multicast. - -Quando outra instância recebe uma mensagem de heartbeat, o `SocketUDP` identifica -o tipo da mensagem e encaminha o objeto ao `HeartbeatManager`, juntamente com o endereço -IP de origem. - -O `HeartbeatManager` registra ou atualiza o peer correspondente, utilizando o -identificador da instância para diferenciá-lo das demais. - -Um peer é considerado inativo quando permanece mais de 15 segundos sem receber um novo -heartbeat. O `HeartbeatManager` então remove o peer da lista e notifica todos os -componentes registrados como listeners de mudanças de peers. - -Esses listeners podem incluir diferentes partes da aplicação, como componentes -da interface gráfica, mecanismos de comunicação (como o `SocketTCP`) e também plugins -que desejem reagir à presença ou ausência de usuários na rede. - -Dessa forma, o sistema permite que tanto o núcleo da aplicação quanto extensões -externas sejam notificados sobre alterações no estado da rede, mantendo a arquitetura -flexível e extensível. - -O fluxo completo é: - -``` -HeartbeatManager -| -| cria HeartbeatMessage -| -v -Message.toByteArray() -| -v -SocketUDP.sendMulticast() -| -v -UDP Multicast -| -v -SocketUDP.receive() -| -v -HeartbeatManager.receiveHeartbeat() -| -v -Lista de peers ativos -| -+----> Interface de usuários online -| -+----> SocketTCP (PeerListener) -| -+----> Plugins (PeerListener) -``` - -## Sistema de plugins - -O sistema de plugins é responsável por permitir a extensão das funcionalidades da -aplicação de forma modular, sem a necessidade de alterações no núcleo do sistema. -Ele define um contrato comum que deve ser seguido por todas as extensões, garantindo -que possam ser carregadas dinamicamente e integradas à comunicação e à interface da -aplicação. - -### Interface Plugin - -A interface `Plugin` define o contrato que deve ser implementado por qualquer -extensão da aplicação. - -Um plugin deve fornecer informações básicas de identificação (`name`, -`author` e `version`), inicializar seu estado através de `createPlugin()` e -participar do sistema de comunicação utilizando mensagens derivadas de -`Message`. - -Os principais métodos são: - -#### Identificação - -Os métodos: -- `getName()` -- `getAuthor()` -- `getVersion()` - -fornecem informações utilizadas pela aplicação para identificar o plugin. - -#### Inicialização - -`createPlugin(MainWindow window)` - -É chamado após o carregamento do plugin e permite que ele registre seus -componentes na aplicação, como menus ou elementos de interface. - -#### Comunicação - -`getMessage()` - -Deve retornar uma mensagem utilizada pelo plugin para comunicação com outras -instâncias da aplicação. - -`receiveMessage(byte[] message)` - -É chamado pela aplicação sempre que uma mensagem é recebida. Cada plugin deve -verificar se a mensagem pertence ao seu tipo e realizar o processamento -correspondente. - -### Descoberta de plugins - -Para serem reconhecidos, os plugins devem possuir os arquivos de configuração -do Java Service Provider Interface (SPI): - -``` -MeusPlugins -| -+---META-INF - | - +---services - | - +---pitiupi.models.Plugin -``` - -O arquivo `pitiupi.models.Plugin` deve conter o nome completo das classes que -implementam a interface Plugin: - -``` -MeusPlugins.plugins.NomeDaClasse -``` - -Para serem carregados, os plugins devem estar empacotados como arquivos JAR -e colocados no diretório plugins da aplicação. - -### Carregamento de plugins - -Ao iniciar a aplicação, o `PluginLoader` executa os seguintes passos: - -1. Procura arquivos `.jar` dentro do diretório `plugins`. -2. Cria um `URLClassLoader` contendo os JARs encontrados. -3. Utiliza `ServiceLoader` para localizar classes que implementam - `Plugin`. -4. Instancia cada plugin encontrado. -5. Chama o método `createPlugin(MainWindow)`. -6. Registra o plugin na lista de plugins ativos da aplicação. - -## Interface gráfica - -A interface gráfica da aplicação é baseada em Swing e é centralizada na -classe `MainWindow`. - -Durante a inicialização, cada plugin recebe uma referência para a -`MainWindow` através do método `createPlugin(MainWindow)`. - -Essa referência permite integrar componentes gráficos à aplicação, sendo a -principal forma de extensão a adição de menus à barra de menus: - -``` -JMenu menu = new JMenu("Meu Plugin"); -window.addMenu(menu); -``` - -Visão geral da comunicação - -A arquitetura de comunicação pode ser resumida da seguinte maneira: -``` - +------------------+ - | MainWindow | - +--------+---------+ - | - +--------------------+--------------------+ - | | | - v v v - HeartbeatManager SocketUDP SocketTCP - | | | - | | | - v v v - Heartbeat UDP Multicast TCP Connections - | | - | | - +---------+----------+ - | - v - Plugins - | - v - Message -``` - -O UDP multicast fornece a infraestrutura de descoberta e manutenção da presença dos -peers, além de permitir comunicação multicast e unicast. - -O TCP fornece canais diretos e persistentes entre peers, com uma conexão reutilizável -por endereço. - -Os dois mecanismos encaminham as mensagens destinadas às funcionalidades da aplicação -para os plugins, mantendo separadas as responsabilidades de transporte, infraestrutura -e lógica específica de cada extensão. \ No newline at end of file +Conteúdo técnico fatiado em [docs/documentation/](../../docs/documentation/README.md). diff --git a/pitiupi/docs/PluginTutorial.md b/pitiupi/docs/PluginTutorial.md index 120dcfd..32a15d0 100644 --- a/pitiupi/docs/PluginTutorial.md +++ b/pitiupi/docs/PluginTutorial.md @@ -1,2 +1,5 @@ -# Como criar plugins para o Share my Sheet +# Tutorial de plugin (legado) +Use o tutorial atualizado: + +**[docs/documentation/10_tutorial_plugin.md](../../docs/documentation/10_tutorial_plugin.md)** diff --git a/pitiupi/docs/README.md b/pitiupi/docs/README.md new file mode 100644 index 0000000..82a000b --- /dev/null +++ b/pitiupi/docs/README.md @@ -0,0 +1,10 @@ +# Documentação movida + +A documentação canônica do Pitiupi está na raiz do repositório: + +**[../../docs/README.md](../../docs/README.md)** + +Arquivos legados nesta pasta: + +- [Documentation.md](Documentation.md) — redirecionamento +- [PluginTutorial.md](PluginTutorial.md) — redirecionamento diff --git a/pitiupi/plugins/README.md b/pitiupi/plugins/README.md new file mode 100644 index 0000000..d640687 --- /dev/null +++ b/pitiupi/plugins/README.md @@ -0,0 +1,18 @@ +# Plugins instalados + +O núcleo carrega arquivos `.jar` desta pasta ao iniciar (relativo ao diretório de trabalho — execute `java -jar dist/pitiupi.jar` a partir de `pitiupi/`). + +## Desenvolvimento + +Após compilar os módulos de referência, use symlinks (Linux): + +```bash +cd pitiupi/plugins +ln -sf ../../pitiupi.chat/dist/pitiupi.chat.jar pitiupi.chat.jar +ln -sf ../../pitiupi.screenCast/dist/pitiupi.screenCast.jar pitiupi.screenCast.jar +ln -sf ../../pitiupi.mpChat/dist/mpChat.jar mpChat.jar +``` + +Ordem de build: núcleo (`pitiupi`) primeiro, depois cada plugin. Ver [docs/documentation/00_quickstart.md](../../docs/documentation/00_quickstart.md). + +Os JARs gerados não devem ser commitados — apenas este README e symlinks locais.