refactor(plugins): SPI workspace-only e remoção de menus legados

Remove GuiManager e pitiupi.models.Menus; atualiza chat/screencast, docs e testes (ConfigLoader, TcpConnectionManager).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Gabriel Felipe Souza SantosandCursor committed 2026-10-27 20:39:39 -03:00
1 parent 7c8a476253
commit 42e70af48b
33 files changed
+103 -443

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@ Entrada na raiz do repositório: [README.md](../README.md).
| --- | --- | | --- | --- |
| **Aluno (TP)** | [documentation/00_quickstart.md](documentation/00_quickstart.md) → [10_tutorial_plugin.md](documentation/10_tutorial_plugin.md) → [17_tutorial_ping_aula.md](documentation/17_tutorial_ping_aula.md) → [11_plugins_referencia.md](documentation/11_plugins_referencia.md) | | **Aluno (TP)** | [documentation/00_quickstart.md](documentation/00_quickstart.md) → [10_tutorial_plugin.md](documentation/10_tutorial_plugin.md) → [17_tutorial_ping_aula.md](documentation/17_tutorial_ping_aula.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) | | **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) | | **Desenvolvedor** | [documentation/13_guia_desenvolvimento.md](documentation/13_guia_desenvolvimento.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) | | **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. **Idioma:** documentação em PT-BR; pacotes e classes Java como no código.
+1 -1
View File
@@ -11,7 +11,7 @@
| 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) | | 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) | | D8 | Classloader por JAR de plugin | Isolar dependências; desserialização com `PluginObjectInputStream` | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
| D9 | Plugins em `PITIUPI_HOME/plugins` | Dados do usuário fora do clone Git; `PITIUPI_HOME` ou `~/pitiupi` | [00_quickstart.md](documentation/00_quickstart.md) | | D9 | Plugins em `PITIUPI_HOME/plugins` | Dados do usuário fora do clone Git; `PITIUPI_HOME` ou `~/pitiupi` | [00_quickstart.md](documentation/00_quickstart.md) |
| D10 | Erros com `ErrorCode` | Mensagens PT na UI; `PitiupiException` no domínio | [13_guia_dev_e_agente.md](documentation/13_guia_dev_e_agente.md) | | D10 | Erros com `ErrorCode` | Mensagens PT na UI; `PitiupiException` no domínio | [13_guia_desenvolvimento.md](documentation/13_guia_desenvolvimento.md) |
| D11 | Reinício após instalar/remover JAR | Mutar `plugins/` exige reiniciar o app para recarregar SPI | [04_inicializacao_e_gui.md](documentation/04_inicializacao_e_gui.md) | | D11 | Reinício após instalar/remover JAR | Mutar `plugins/` exige reiniciar o app para recarregar SPI | [04_inicializacao_e_gui.md](documentation/04_inicializacao_e_gui.md) |
| D16 | UI em abas; sem `JFrame` de plugin | Conteúdo na `MainShellFrame`; galeria e peer actions abrem abas | [04_inicializacao_e_gui.md](documentation/04_inicializacao_e_gui.md) | | D16 | UI em abas; sem `JFrame` de plugin | Conteúdo na `MainShellFrame`; galeria e peer actions abrem abas | [04_inicializacao_e_gui.md](documentation/04_inicializacao_e_gui.md) |
| D17 | Goodbye + timeout de presença | `GoodbyeMessage` em multicast ao fechar; timeout ~15 s mantido | [08_heartbeat_e_presenca.md](documentation/08_heartbeat_e_presenca.md) | | D17 | Goodbye + timeout de presença | `GoodbyeMessage` em multicast ao fechar; timeout ~15 s mantido | [08_heartbeat_e_presenca.md](documentation/08_heartbeat_e_presenca.md) |
@@ -16,7 +16,7 @@ Cada equipe/aluno entrega um **módulo Java separado** que:
1. Compila para JAR e carrega via `pitiupi/plugins/`. 1. Compila para JAR e carrega via `pitiupi/plugins/`.
2. Define mensagens (`extends Message`) e o fluxo entre peers. 2. Define mensagens (`extends Message`) e o fluxo entre peers.
3. Integra menus/janelas via `GuiManager`. 3. Integra UI na aba central via `PluginWorkspace` e peer actions na lista lateral.
4. Inclui README do módulo (build + protocolo). 4. Inclui README do módulo (build + protocolo).
Exemplos de tema: chat temático, tabuleiro (xadrez/damas), compartilhamento de arquivo, quiz síncrono. Exemplos de tema: chat temático, tabuleiro (xadrez/damas), compartilhamento de arquivo, quiz síncrono.
+4 -4
View File
@@ -1,7 +1,8 @@
# Sistema de plugins # Sistema de plugins
O sistema de plugins permite extensão modular sem alterar o núcleo. O contrato O sistema de plugins permite extensão modular sem alterar o núcleo. O contrato está em `pitiupi.models.Plugin` (classe abstrata).
está em `pitiupi.models.Plugin` (classe abstrata).
**Breaking change (pós-v1):** plugins devem implementar apenas `createPlugin(ConnectionManager, PluginWorkspace)`. A API antiga com `GuiManager` e menus por categoria foi removida.
## Interface Plugin ## Interface Plugin
@@ -11,8 +12,7 @@ Métodos obrigatórios:
| --- | --- | | --- | --- |
| `getName()`, `getAuthor()`, `getVersion()`, `getIdentifier()` | Identificação (o `identifier` distingue plugins em conexões TCP dedicadas) | | `getName()`, `getAuthor()`, `getVersion()`, `getIdentifier()` | Identificação (o `identifier` distingue plugins em conexões TCP dedicadas) |
| `getDisplayName()` | Texto na galeria e título de aba (padrão: `getName()`) | | `getDisplayName()` | Texto na galeria e título de aba (padrão: `getName()`) |
| `createPlugin(ConnectionManager, PluginWorkspace)` | Inicialização na shell: painel na aba, peer actions, rede | | `createPlugin(ConnectionManager, PluginWorkspace)` | Inicialização: `bindShellWorkspaceContent`, `installPeerActions`, rede |
| `createPlugin(ConnectionManager, GuiManager)` | Legado — adapter na carga; não usar em plugins novos |
| `getMessages()` | Lista de classes `Message` que o plugin desserializa | | `getMessages()` | Lista de classes `Message` que o plugin desserializa |
| `receiveMessage(Message)` | Processamento após o `PluginManager` identificar o tipo | | `receiveMessage(Message)` | Processamento após o `PluginManager` identificar o tipo |
+24 -39
View File
@@ -12,9 +12,24 @@ Este guia complementa [09_sistema_de_plugins.md](09_sistema_de_plugins.md). Use
```java ```java
public class MeuPlugin extends Plugin { public class MeuPlugin extends Plugin {
private PluginWorkspace workspace;
@Override @Override
public void createPlugin(ConnectionManager cm, GuiManager gui) { public void createPlugin(ConnectionManager cm, PluginWorkspace workspace) {
// registrar menus, abrir janelas this.workspace = workspace;
MeuPanel panel = new MeuPanel(cm);
bindShellWorkspaceContent(panel);
installPeerActions(workspace.peerActions());
}
@Override
public void installPeerActions(PeerActionsRegistrar registrar) {
registrar.register(PeerAction.of(
"meuplugin.abrir",
"Abrir",
PeerActionTarget.ALL_PEERS,
target -> workspace.openOrFocusTab(getIdentifier(), getDisplayName())
));
} }
@Override @Override
@@ -49,25 +64,9 @@ Arquivo `src/META-INF/services/pitiupi.models.Plugin`:
pacote.MeuPlugin pacote.MeuPlugin
``` ```
## 5. Categoria no menu ## 5. Registrar no catálogo
Escolha onde o plugin aparece na barra **Plugin**: Adicione uma entrada em [`plugins.registry.json`](../../plugins.registry.json):
| `MenuCategory` | Uso |
| --- | --- |
| `Social` | Chat, presença |
| `Jogos` | Jogos entre peers |
| `Streaming` | Screen cast, mídia |
Exemplo:
```java
guiManager.createMenu(MenuCategory.Jogos, new MeuMenu(...));
```
## 6. Registrar no catálogo
Adicione uma entrada em [`plugins.registry.json`](../../plugins.registry.json) na raiz do repo:
```json ```json
{ {
@@ -81,29 +80,15 @@ Adicione uma entrada em [`plugins.registry.json`](../../plugins.registry.json) n
O `id` é o que você passa para `./scripts/pitiupi.sh run --plugins xadrez`. O `id` é o que você passa para `./scripts/pitiupi.sh run --plugins xadrez`.
## 7. Build e instalação ## 6. Build e instalação
```bash ```bash
./scripts/pitiupi.sh build --plugins xadrez ./scripts/pitiupi.sh build --plugins xadrez
# ou, manualmente:
cd pitiupi.xadrez && ant jar
ln -sf ../../pitiupi.xadrez/dist/pitiupi.xadrez.jar ../pitiupi/plugins/xadrez.jar
``` ```
Reinicie o Pitiupi após trocar JARs. JARs de runtime ficam em `PITIUPI_HOME/plugins` (ver [00_quickstart.md](00_quickstart.md)).
## 8. Teste ## 7. Próximos passos
Duas instâncias, mesma rede, portas distintas se no mesmo host. Ver [00_quickstart.md](00_quickstart.md). - [11_plugins_referencia.md](11_plugins_referencia.md) — chat e screenCast
- [17_tutorial_ping_aula.md](17_tutorial_ping_aula.md) — plugin didático Ping (opcional, fora do commit)
## TCP dedicado (avançado)
Se o plugin precisa de canal TCP com handshake por `getIdentifier()`, estude `plugins/chat` (`receivePrivateConnection`, `receiveBytes`). Use `MessageDispatcher` para envios didáticos. 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) |
@@ -1,34 +1,15 @@
# Menus, peer actions e transportes # Menus e ações na lista (histórico)
## Interface atual (shell v1) Na v1 atual, plugins **não** registram menus na barra superior nem submenus legados por categoria.
Plugins de referência **não** dependem da barra de menus superior para o fluxo principal: ## Como expor ações hoje
- **Galeria** na aba **Plugins** abre cada extensão em aba própria (fechável com ×). 1. **Galeria Plugins** — abre a aba central do plugin.
- **Peer actions** na lista **Usuários nesta rede** (clique em um peer ou em **Todos**) agrupam ações de todos os plugins instalados. 2. **Lista lateral** — clique em um peer ou em **Todos** → menu com **peer actions** registradas por cada plugin (`PeerActionBroker`).
- Dentro da aba do plugin, **`PluginRoutingPanel`** padroniza destino (sala geral vs IP), forma de envio (multicast UDP, UDP unicast, TCP) e texto de ajuda. 3. **`PluginWorkspace.openOrFocusTab`** — dentro do handler da peer action ou após carregar o painel.
Ver [04_inicializacao_e_gui.md](04_inicializacao_e_gui.md) e [09_sistema_de_plugins.md](09_sistema_de_plugins.md). ## Catálogo `menuCategory`
## MenuCategory (legado) O campo `menuCategory` em [`plugins.registry.json`](../../plugins.registry.json) (`Social`, `Jogos`, `Streaming`) serve apenas ao script `./scripts/pitiupi.sh` para filtrar build/run — não cria menus Swing.
Enum em `pitiupi.models.Menus.MenuCategory` (`Jogos`, `Social`, `Streaming`) ainda aparece no **catálogo** (`menuCategory` em `plugins.registry.json`) e em plugins antigos que chamam `GuiManager.createMenu(...)`. Referência de plugins: [11_plugins_referencia.md](11_plugins_referencia.md).
Novos plugins devem preferir `PluginWorkspace` + peer actions. O menu da shell expõe apenas **Ajuda → Sobre**.
## Unicast vs multicast na aplicação
| Modo | API típica | Semântica |
| --- | --- | --- |
| Multicast | `MessageDispatcher` / `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 com framing 4+N bytes |
Detalhes de rede: [05_udp_multicast.md](05_udp_multicast.md), [06_tcp_conexoes.md](06_tcp_conexoes.md).
## Referência de código
- Chat e screencast: peer actions em `ChatPlugin` e `ScreenCastPlugin`.
- Envio unificado: `pitiupi.application.messaging.MessageDispatcher` e `TransportMode`.
O antigo plugin **mpChat** foi unificado no módulo `plugins/chat` (mensagens `LegacyMpChatMessage` para compatibilidade TCP privado). Ver [11_plugins_referencia.md](11_plugins_referencia.md) e decisão D19 no [decision-register.md](../decision-register.md).
@@ -1,6 +1,6 @@
# Guia para desenvolvedor e agente # Guia para desenvolvimento
Esta documentação descreve **apenas** a ferramenta Pitiupi neste repositório: build, rede, plugins e convenções de código. Quem clona o projeto não precisa de pastas ou ferramentas de meta-desenvolvimento externas; mantenha referências técnicas e tutoriais limitados ao que está em `docs/` e no código do próprio repo. Esta documentação descreve a ferramenta Pitiupi neste repositório: build, rede, plugins e convenções de código.
## Leitura mínima antes de mudar código ## Leitura mínima antes de mudar código
@@ -21,23 +21,21 @@ Esta documentação descreve **apenas** a ferramenta Pitiupi neste repositório:
## Gate de build ## Gate de build
Compilar núcleo e os dois plugins do catálogo:
```bash ```bash
cd pitiupi && ant jar && ant test cd pitiupi && ant jar && ant test
cd plugins/chat && ant jar cd plugins/chat && ant jar
cd plugins/screencast && ant jar cd plugins/screencast && ant jar
``` ```
Ou na raiz do clone: `./scripts/pitiupi.sh build --all`. Ou na raiz: `./scripts/pitiupi.sh build --all`.
Requer JDK e Ant no PATH. Teste manual em rede: [15_testes_e_gate.md](15_testes_e_gate.md). Testes manuais em rede: [15_testes_e_gate.md](15_testes_e_gate.md).
## Convenções ## Convenções
- Código e identificadores em inglês (pacotes `pitiupi.*`). - Código e identificadores em inglês (pacotes `pitiupi.*`).
- Documentação em português (BR) em `docs/`. - Documentação em português (BR) em `docs/`.
- Evitar alterar o núcleo para necessidades de um único plugin — preferir extensão via `Plugin`. - Evitar alterar o núcleo para um único plugin — preferir extensão via `Plugin` e `PluginWorkspace`.
## Decisões e débitos ## Decisões e débitos
@@ -10,7 +10,6 @@
| Usuários online, peer actions | `pitiupi.presentation.shell.NetworkUsersPanel`, `PeerActionBroker` | | Usuários online, peer actions | `pitiupi.presentation.shell.NetworkUsersPanel`, `PeerActionBroker` |
| Facade rede para plugins | `pitiupi.control.ConnectionManager` | | Facade rede para plugins | `pitiupi.control.ConnectionManager` |
| Workspace na aba (SPI) | `pitiupi.api.plugin.PluginWorkspace`, `ShellPluginWorkspace` | | Workspace na aba (SPI) | `pitiupi.api.plugin.PluginWorkspace`, `ShellPluginWorkspace` |
| Menus legados (adapter) | `pitiupi.control.GuiManager` — deprecado; preferir peer actions |
| UDP | `pitiupi.net.udp.SocketUDP` | | UDP | `pitiupi.net.udp.SocketUDP` |
| TCP servidor e mapa de conexões | `pitiupi.net.tcp.SocketTCP`, `TcpConnectionManager` | | TCP servidor e mapa de conexões | `pitiupi.net.tcp.SocketTCP`, `TcpConnectionManager` |
| Conexão TCP por peer | `pitiupi.net.tcp.TcpConnection` | | Conexão TCP por peer | `pitiupi.net.tcp.TcpConnection` |
@@ -18,8 +17,6 @@
| Heartbeat | `pitiupi.control.HeartbeatManager`, `HeartbeatMessage` | | Heartbeat | `pitiupi.control.HeartbeatManager`, `HeartbeatMessage` |
| Carregar JARs, despacho | `pitiupi.control.plugins.PluginManager` | | Carregar JARs, despacho | `pitiupi.control.plugins.PluginManager` |
| Contrato plugin | `pitiupi.models.Plugin`, `pitiupi.models.Messages.Message` | | Contrato plugin | `pitiupi.models.Plugin`, `pitiupi.models.Messages.Message` |
| Menus por categoria | `pitiupi.models.Menus.*` |
## Dependências entre módulos ## Dependências entre módulos
``` ```
+2
View File
@@ -10,6 +10,8 @@ cd pitiupi && ant test
Falha se `java`, `javac` ou Ant estiverem ausentes. Falha se `java`, `javac` ou Ant estiverem ausentes.
Pacotes com testes automatizados (JUnit 5): `platform` (`PitiupiHome`, `ConfigLoader`), `plugins.store`, `application.presence`, `application.peeractions`, `application.invites`, `application.messaging`, `application.activity`, `presentation.shell`, `control` (goodbye), `net.tcp` (fechamento de conexões), `models`.
## Gate de build (produto) ## Gate de build (produto)
Na raiz do clone: Na raiz do clone:
+1 -1
View File
@@ -20,7 +20,7 @@ Referência para **código Java**, **rede P2P** e **plugins**. Índice geral: [d
| 11 | [11_plugins_referencia.md](11_plugins_referencia.md) | chat e screencast | | 11 | [11_plugins_referencia.md](11_plugins_referencia.md) | chat e screencast |
| 17 | [17_tutorial_ping_aula.md](17_tutorial_ping_aula.md) | Exercício Ping (aula) | | 17 | [17_tutorial_ping_aula.md](17_tutorial_ping_aula.md) | Exercício Ping (aula) |
| 12 | [12_menus_unicast_multicast.md](12_menus_unicast_multicast.md) | MenuCategory e ações | | 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 | | 13 | [13_guia_desenvolvimento.md](13_guia_desenvolvimento.md) | Guia de desenvolvimento |
| 14 | [14_arquitetura_e_manutencao.md](14_arquitetura_e_manutencao.md) | Onde editar cada comportamento | | 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 | | 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 | | 16 | [16_fundamentacao_redes.md](16_fundamentacao_redes.md) | Teoria ↔ código |
@@ -4,7 +4,6 @@ import pitiupi.application.invites.InviteInboxService;
import pitiupi.application.peeractions.PeerActionBroker; import pitiupi.application.peeractions.PeerActionBroker;
import pitiupi.application.peeractions.PluginPeerActionsRegistrar; import pitiupi.application.peeractions.PluginPeerActionsRegistrar;
import pitiupi.control.ConnectionManager; import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.models.Plugin; import pitiupi.models.Plugin;
import pitiupi.presentation.shell.ShellHost; import pitiupi.presentation.shell.ShellHost;
import pitiupi.presentation.shell.TabWorkspacePanel; import pitiupi.presentation.shell.TabWorkspacePanel;
@@ -12,14 +11,13 @@ import pitiupi.presentation.shell.TabWorkspacePanel;
import javax.swing.JComponent; import javax.swing.JComponent;
/** /**
* Workspace com abas e barra de status (Onda D). * Workspace com abas e barra de status.
*/ */
public final class ShellPluginWorkspace implements PluginWorkspace { public final class ShellPluginWorkspace implements PluginWorkspace {
private final String pluginId; private final String pluginId;
private final Plugin plugin; private final Plugin plugin;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final GuiManager guiManager;
private final TabWorkspacePanel tabWorkspace; private final TabWorkspacePanel tabWorkspace;
private final ShellHost shellHost; private final ShellHost shellHost;
private final PluginPeerActionsRegistrar registrar; private final PluginPeerActionsRegistrar registrar;
@@ -28,7 +26,6 @@ public final class ShellPluginWorkspace implements PluginWorkspace {
String pluginId, String pluginId,
Plugin plugin, Plugin plugin,
ConnectionManager connectionManager, ConnectionManager connectionManager,
GuiManager guiManager,
PeerActionBroker peerActionBroker, PeerActionBroker peerActionBroker,
InviteInboxService inviteInboxService, InviteInboxService inviteInboxService,
TabWorkspacePanel tabWorkspace, TabWorkspacePanel tabWorkspace,
@@ -37,7 +34,6 @@ public final class ShellPluginWorkspace implements PluginWorkspace {
this.pluginId = pluginId; this.pluginId = pluginId;
this.plugin = plugin; this.plugin = plugin;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.guiManager = guiManager;
this.tabWorkspace = tabWorkspace; this.tabWorkspace = tabWorkspace;
this.shellHost = shellHost; this.shellHost = shellHost;
this.registrar = new PluginPeerActionsRegistrar(peerActionBroker, pluginId); this.registrar = new PluginPeerActionsRegistrar(peerActionBroker, pluginId);
@@ -46,10 +42,6 @@ public final class ShellPluginWorkspace implements PluginWorkspace {
inviteInboxService.setInviteHandler(pluginId, plugin::onInviteAccepted); inviteInboxService.setInviteHandler(pluginId, plugin::onInviteAccepted);
} }
public GuiManager guiManager() {
return guiManager;
}
@Override @Override
public ConnectionManager connectionManager() { public ConnectionManager connectionManager() {
return connectionManager; return connectionManager;
@@ -30,12 +30,6 @@ public final class PeerActivityInbox {
}; };
} }
/** @deprecated use {@link #recordCounted} */
@Deprecated
public void record(String peerInstanceId, String pluginId, String peerActionId) {
recordCounted(peerInstanceId, pluginId, peerActionId, null);
}
public void recordBroadcast(String pluginId, String peerActionId) { public void recordBroadcast(String pluginId, String peerActionId) {
recordCounted(BROADCAST_PEER_KEY, pluginId, peerActionId, PeerActivityFocusTokens.broadcast()); recordCounted(BROADCAST_PEER_KEY, pluginId, peerActionId, PeerActivityFocusTokens.broadcast());
} }
@@ -74,12 +68,6 @@ public final class PeerActivityInbox {
return sum(countedByPeerAndAction.getOrDefault(peerKey(peerInstanceId), Map.of())); return sum(countedByPeerAndAction.getOrDefault(peerKey(peerInstanceId), Map.of()));
} }
/** @deprecated use {@link #totalCountedForPeer} */
@Deprecated
public int totalForPeer(String peerInstanceId) {
return totalCountedForPeer(peerInstanceId);
}
public boolean hasIndicatorForPeer(String peerInstanceId) { public boolean hasIndicatorForPeer(String peerInstanceId) {
Map<String, Integer> map = indicatorByPeerAndAction.get(peerKey(peerInstanceId)); Map<String, Integer> map = indicatorByPeerAndAction.get(peerKey(peerInstanceId));
return map != null && !map.isEmpty(); return map != null && !map.isEmpty();
@@ -1,80 +0,0 @@
package pitiupi.control;
import pitiupi.presentation.shell.NetworkUsersPanel;
import pitiupi.presentation.shell.ShellHost;
import pitiupi.models.Menus.*;
import pitiupi.models.Menus.MulticastMenus.MulticastActionMenu;
import pitiupi.models.Menus.MulticastMenus.MulticastMenu;
import pitiupi.models.Menus.MulticastMenus.MulticastSubMenu;
import pitiupi.models.Menus.UnicastMenus.UnicastActionMenu;
import pitiupi.models.Menus.UnicastMenus.UnicastMenu;
import pitiupi.models.Menus.UnicastMenus.UnicastSubMenu;
import javax.swing.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class GuiManager {
private final ShellHost shellHost;
private final NetworkUsersPanel networkUsersPanel;
private Logger logger;
public GuiManager(ShellHost shellHost, javax.swing.JMenuBar menuBar, NetworkUsersPanel networkUsersPanel) {
this.shellHost = shellHost;
this.networkUsersPanel = networkUsersPanel;
this.logger = Logger.getLogger(GuiManager.class.getName());
}
public void createMenu(MenuCategory category, Menu menu) {
try {
JMenuItem newMenu = buildMenu(menu);
if (menu instanceof MulticastMenu) {
// Onda D: ações multicast migram para peer actions / abas (Onda E).
}
if (menu instanceof UnicastMenu) {
networkUsersPanel.addUnicastMenu(category, newMenu);
}
}
catch (IllegalArgumentException e) {
logger.log(Level.WARNING, e.getMessage());
}
}
private JMenuItem buildMenu(Menu menu) {
if (menu instanceof MulticastSubMenu multicastSubMenu) {
JMenu newMenu = new JMenu(menu.getText());
for (Menu child : multicastSubMenu.getMenus()) {
newMenu.add(buildMenu(child));
}
return newMenu;
}
if (menu instanceof UnicastSubMenu unicastSubMenu) {
JMenu newMenu = new JMenu(menu.getText());
for (Menu child : unicastSubMenu.getMenus()) {
newMenu.add(buildMenu(child));
}
return newMenu;
}
if (menu instanceof MulticastActionMenu multicastActionMenu) {
JMenuItem newMenu = new JMenuItem(menu.getText());
newMenu.addActionListener(e -> multicastActionMenu.onClick());
return newMenu;
}
if (menu instanceof UnicastActionMenu unicastActionMenu) {
JMenuItem newMenu = new JMenuItem(menu.getText());
networkUsersPanel.initUnicastMenu(newMenu, unicastActionMenu::onClick);
return newMenu;
}
throw new IllegalArgumentException("Invalid menu type");
}
}
@@ -4,7 +4,6 @@ import pitiupi.Main;
import pitiupi.api.plugin.PluginWorkspace; import pitiupi.api.plugin.PluginWorkspace;
import pitiupi.models.Plugin; import pitiupi.models.Plugin;
import pitiupi.control.ConnectionManager; import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.errors.ErrorCode; import pitiupi.errors.ErrorCode;
import pitiupi.models.Messages.Message; import pitiupi.models.Messages.Message;
import pitiupi.platform.PitiupiHome; import pitiupi.platform.PitiupiHome;
@@ -112,7 +111,6 @@ public class PluginManager {
public void loadPlugins( public void loadPlugins(
ConnectionManager connectionManager, ConnectionManager connectionManager,
GuiManager guiManager,
Function<Plugin, PluginWorkspace> workspaceFactory Function<Plugin, PluginWorkspace> workspaceFactory
) { ) {
File pluginDir = PitiupiHome.get().pluginsDirectory().toFile(); File pluginDir = PitiupiHome.get().pluginsDirectory().toFile();
@@ -1,5 +0,0 @@
package pitiupi.models.Menus;
public abstract class Menu {
public abstract String getText();
}
@@ -1,7 +0,0 @@
package pitiupi.models.Menus;
public enum MenuCategory {
Jogos,
Social,
Streaming
}
@@ -1,5 +0,0 @@
package pitiupi.models.Menus.MulticastMenus;
public abstract class MulticastActionMenu extends MulticastMenu{
public abstract void onClick();
}
@@ -1,9 +0,0 @@
package pitiupi.models.Menus.MulticastMenus;
import pitiupi.models.Menus.Menu;
public abstract class MulticastMenu extends Menu {
MulticastMenu() {
super();
}
}
@@ -1,7 +0,0 @@
package pitiupi.models.Menus.MulticastMenus;
import java.util.List;
public abstract class MulticastSubMenu extends MulticastMenu{
public abstract List<MulticastMenu> getMenus();
}
@@ -1,7 +0,0 @@
package pitiupi.models.Menus.UnicastMenus;
import pitiupi.models.User;
public abstract class UnicastActionMenu extends UnicastMenu{
public abstract void onClick(User user);
}
@@ -1,8 +0,0 @@
package pitiupi.models.Menus.UnicastMenus;
import pitiupi.models.Menus.Menu;
import pitiupi.models.User;
public abstract class UnicastMenu extends Menu {
UnicastMenu() {}
}
@@ -1,7 +0,0 @@
package pitiupi.models.Menus.UnicastMenus;
import java.util.List;
public abstract class UnicastSubMenu extends UnicastMenu{
public abstract List<UnicastMenu> getMenus();
}
+2 -29
View File
@@ -7,9 +7,7 @@ package pitiupi.models;
import pitiupi.api.plugin.InviteDetails; import pitiupi.api.plugin.InviteDetails;
import pitiupi.api.plugin.PeerActionsRegistrar; import pitiupi.api.plugin.PeerActionsRegistrar;
import pitiupi.api.plugin.PluginWorkspace; import pitiupi.api.plugin.PluginWorkspace;
import pitiupi.api.plugin.ShellPluginWorkspace;
import pitiupi.control.ConnectionManager; import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.models.Messages.Message; import pitiupi.models.Messages.Message;
import pitiupi.net.tcp.TcpConnection; import pitiupi.net.tcp.TcpConnection;
@@ -54,24 +52,9 @@ public abstract class Plugin {
} }
/** /**
* Inicializa o plugin. * Inicializa o plugin na aba central (shell) e registra peer actions.
*
* <p>Este método é chamado pela aplicação após o carregamento do plugin.</p>
*
* @param connectionManager interface com o sistema de conexões da aplicação.
* @param guiManager interface com a janela principal da aplicação
*/ */
public abstract void createPlugin(ConnectionManager connectionManager, GuiManager guiManager); public abstract void createPlugin(ConnectionManager connectionManager, PluginWorkspace workspace);
/**
* Inicialização via workspace (Onda C+). O adaptador chama o legado e {@link #installPeerActions}.
*/
public void createPlugin(ConnectionManager connectionManager, PluginWorkspace workspace) {
if (workspace instanceof ShellPluginWorkspace shell) {
createPlugin(connectionManager, shell.guiManager());
installPeerActions(workspace.peerActions());
}
}
public void installPeerActions(PeerActionsRegistrar registrar) { public void installPeerActions(PeerActionsRegistrar registrar) {
} }
@@ -87,19 +70,9 @@ public abstract class Plugin {
/** /**
* Retorna uma mensagem utilizada pelo plugin. * Retorna uma mensagem utilizada pelo plugin.
*
* @return mensagem criada pelo plugin.
*/ */
public abstract List<Class<? extends Message>> getMessages(); public abstract List<Class<? extends Message>> getMessages();
/**
* Recebe uma mensagem enviada pela aplicação.
*
* <p>A mensagem é recebida em formato serializado. O plugin deve verificar
* se a mensagem pertence ao seu tipo e realizar o processamento necessário.</p>
*
* @param message mensagem serializada recebida.
*/
public abstract void receiveMessage(Message message); public abstract void receiveMessage(Message message);
public boolean receiveAllTypes() {return false;} public boolean receiveAllTypes() {return false;}
@@ -2,7 +2,6 @@ package pitiupi.presentation.shell;
import pitiupi.api.plugin.ShellPluginWorkspace; import pitiupi.api.plugin.ShellPluginWorkspace;
import pitiupi.control.ConnectionManager; import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.control.plugins.PluginManager; import pitiupi.control.plugins.PluginManager;
import pitiupi.models.Plugin; import pitiupi.models.Plugin;
@@ -22,7 +21,6 @@ public final class MainShellFrame extends JFrame implements ShellHost {
private final PluginManager pluginManager = new PluginManager(); private final PluginManager pluginManager = new PluginManager();
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final NetworkUsersPanel networkUsersPanel; private final NetworkUsersPanel networkUsersPanel;
private final GuiManager guiManager;
private final TabWorkspacePanel tabWorkspace; private final TabWorkspacePanel tabWorkspace;
private final PluginGalleryPanel galleryPanel; private final PluginGalleryPanel galleryPanel;
private final SettingsPanel settingsPanel; private final SettingsPanel settingsPanel;
@@ -49,8 +47,6 @@ public final class MainShellFrame extends JFrame implements ShellHost {
ShellMenuBar menuBar = new ShellMenuBar(); ShellMenuBar menuBar = new ShellMenuBar();
setJMenuBar(menuBar); setJMenuBar(menuBar);
guiManager = new GuiManager(this, menuBar, networkUsersPanel);
JPanel center = new JPanel(new BorderLayout()); JPanel center = new JPanel(new BorderLayout());
center.add(tabWorkspace.getRootPanel(), BorderLayout.CENTER); center.add(tabWorkspace.getRootPanel(), BorderLayout.CENTER);
getContentPane().add(center, BorderLayout.CENTER); getContentPane().add(center, BorderLayout.CENTER);
@@ -90,11 +86,10 @@ public final class MainShellFrame extends JFrame implements ShellHost {
} }
private void loadPlugins() { private void loadPlugins() {
pluginManager.loadPlugins(connectionManager, guiManager, (plugin) -> new ShellPluginWorkspace( pluginManager.loadPlugins(connectionManager, (plugin) -> new ShellPluginWorkspace(
plugin.getIdentifier(), plugin.getIdentifier(),
plugin, plugin,
connectionManager, connectionManager,
guiManager,
connectionManager.getPeerActionBroker(), connectionManager.getPeerActionBroker(),
connectionManager.getInviteInboxService(), connectionManager.getInviteInboxService(),
tabWorkspace, tabWorkspace,
@@ -7,9 +7,7 @@ import pitiupi.application.peeractions.PeerActionTarget;
import pitiupi.application.peeractions.PeerTarget; import pitiupi.application.peeractions.PeerTarget;
import pitiupi.application.presence.UserPeerView; import pitiupi.application.presence.UserPeerView;
import pitiupi.control.ConnectionManager; import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.control.PeerListener; import pitiupi.control.PeerListener;
import pitiupi.models.Menus.MenuCategory;
import pitiupi.models.User; import pitiupi.models.User;
import pitiupi.presentation.shell.ui.ShellUi; import pitiupi.presentation.shell.ui.ShellUi;
@@ -28,10 +26,7 @@ import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/** /**
* Lista lateral com Todos e peer actions (sem badges de atividade na v1). * Lista lateral com Todos e peer actions (sem badges de atividade na v1).
@@ -40,8 +35,6 @@ public final class NetworkUsersPanel extends JPanel implements PeerListener {
private final DefaultListModel<DisplayPeer> listModel = new DefaultListModel<>(); private final DefaultListModel<DisplayPeer> listModel = new DefaultListModel<>();
private final JList<DisplayPeer> userList = new JList<>(listModel); private final JList<DisplayPeer> userList = new JList<>(listModel);
private final JPopupMenu menu = new JPopupMenu();
private final Map<MenuCategory, JMenu> legacyCategories = new HashMap<>();
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
public NetworkUsersPanel(ConnectionManager connectionManager) { public NetworkUsersPanel(ConnectionManager connectionManager) {
@@ -49,12 +42,6 @@ public final class NetworkUsersPanel extends JPanel implements PeerListener {
setLayout(new BorderLayout()); setLayout(new BorderLayout());
setPreferredSize(new Dimension(200, 0)); setPreferredSize(new Dimension(200, 0));
for (MenuCategory category : MenuCategory.values()) {
JMenu cat = new JMenu(category.toString());
menu.add(cat);
legacyCategories.put(category, cat);
}
userList.setCellRenderer(new DefaultListCellRenderer() { userList.setCellRenderer(new DefaultListCellRenderer() {
@Override @Override
public Component getListCellRendererComponent( public Component getListCellRendererComponent(
@@ -114,40 +101,13 @@ public final class NetworkUsersPanel extends JPanel implements PeerListener {
} }
popup.add(pluginMenu); popup.add(pluginMenu);
} }
for (JMenu legacy : legacyCategories.values()) {
if (legacy.getItemCount() > 0 && target == PeerActionTarget.SINGLE_PEER && !peer.view.isBroadcastTarget()) {
popup.addSeparator();
JMenu copy = new JMenu(legacy.getText());
for (int i = 0; i < legacy.getItemCount(); i++) {
copy.add(legacy.getItem(i));
}
popup.add(copy);
}
}
if (popup.getComponentCount() > 0) { if (popup.getComponentCount() > 0) {
popup.show(userList, x, y); popup.show(userList, x, y);
} }
} }
public void initUnicastMenu(JMenuItem menuItem, Consumer<User> action) {
menuItem.addActionListener(e -> {
DisplayPeer selected = userList.getSelectedValue();
if (selected == null || selected.view.isBroadcastTarget()) {
return;
}
User user = selected.toUser();
if (user.getAddress() != null) {
action.accept(user);
}
});
}
public void addUnicastMenu(MenuCategory category, JMenuItem menuItem) {
legacyCategories.get(category).add(menuItem);
}
@Override @Override
public void onPeersChanged(List<User> peers) { public void onPeersChanged(List<pitiupi.models.User> peers) {
refreshFromPresence(); refreshFromPresence();
} }
@@ -160,7 +120,7 @@ public final class NetworkUsersPanel extends JPanel implements PeerListener {
}); });
} }
record DisplayPeer(UserPeerView view) { private record DisplayPeer(UserPeerView view) {
@Override @Override
public String toString() { public String toString() {
return view.getDisplayName(); return view.getDisplayName();
@@ -1,6 +1,8 @@
package pitiupi.models; package pitiupi.models;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import pitiupi.api.plugin.PluginWorkspace;
import pitiupi.control.ConnectionManager;
import pitiupi.models.Messages.Message; import pitiupi.models.Messages.Message;
import javax.swing.JPanel; import javax.swing.JPanel;
@@ -36,8 +38,7 @@ class PluginDisplayNameTest {
} }
@Override @Override
public void createPlugin(pitiupi.control.ConnectionManager connectionManager, public void createPlugin(ConnectionManager connectionManager, PluginWorkspace workspace) {
pitiupi.control.GuiManager guiManager) {
} }
@Override @Override
@@ -77,8 +78,7 @@ class PluginDisplayNameTest {
} }
@Override @Override
public void createPlugin(pitiupi.control.ConnectionManager connectionManager, public void createPlugin(ConnectionManager connectionManager, PluginWorkspace workspace) {
pitiupi.control.GuiManager guiManager) {
} }
@Override @Override
@@ -0,0 +1,13 @@
package pitiupi.net.tcp;
import org.junit.jupiter.api.Test;
class TcpConnectionManagerTest {
@Test
void closeAllConnectionsOnEmptyManagerDoesNotThrow() {
TcpConnectionManager manager = new TcpConnectionManager();
manager.closeAllConnections();
manager.closeAllConnections("reason");
}
}
@@ -0,0 +1,32 @@
package pitiupi.platform;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ConfigLoaderTest {
@Test
void loadOrCreateDefaultsWritesConfig(@TempDir Path home) throws Exception {
ConfigLoader loader = new ConfigLoader(home);
loader.loadOrCreateDefaults();
assertTrue(Files.isRegularFile(home.resolve("config.properties")));
assertTrue(loader.isMigrationCwdPluginsPromptEnabled());
assertFalse(loader.isMigrationCwdPluginsDone());
}
@Test
void setMigrationFlagsPersist(@TempDir Path home) throws Exception {
ConfigLoader loader = new ConfigLoader(home);
loader.loadOrCreateDefaults();
loader.setMigrationCwdPluginsDone(true);
ConfigLoader reloaded = new ConfigLoader(home);
reloaded.loadOrCreateDefaults();
assertTrue(reloaded.isMigrationCwdPluginsDone());
}
}
@@ -1,32 +0,0 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package pitiupi.chat.GUI;
import pitiupi.models.Menus.MulticastMenus.MulticastMenu;
import pitiupi.models.Menus.MulticastMenus.MulticastSubMenu;
import java.util.List;
/**
*
* @author flavio
*/
public class ChatMenu extends MulticastSubMenu {
ChatMulticastMenu chatMulticastMenu;
public ChatMenu(ChatWindow chatWindow) {
this.chatMulticastMenu = new ChatMulticastMenu(chatWindow);
}
@Override
public String getText() {
return "Chat";
}
@Override
public List<MulticastMenu> getMenus() {
return List.of(chatMulticastMenu);
}
}
@@ -1,21 +0,0 @@
package pitiupi.chat.GUI;
import pitiupi.models.Menus.MulticastMenus.MulticastActionMenu;;
public class ChatMulticastMenu extends MulticastActionMenu {
ChatWindow chatWindow;
public ChatMulticastMenu(ChatWindow chatWindow) {
this.chatWindow = chatWindow;
}
@Override
public String getText() {
return "Show Chat Window";
}
@Override
public void onClick() {
chatWindow.setVisible(true);
}
}
@@ -12,7 +12,6 @@ import pitiupi.chat.control.PrivateChatHost;
import pitiupi.chat.net.ChatMessage; import pitiupi.chat.net.ChatMessage;
import pitiupi.chat.net.LegacyMpChatMessage; import pitiupi.chat.net.LegacyMpChatMessage;
import pitiupi.control.ConnectionManager; import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.models.Messages.Message; import pitiupi.models.Messages.Message;
import pitiupi.models.Plugin; import pitiupi.models.Plugin;
import pitiupi.models.User; import pitiupi.models.User;
@@ -44,10 +43,6 @@ public class ChatPlugin extends Plugin implements PrivateChatHost {
installPeerActions(workspace.peerActions()); installPeerActions(workspace.peerActions());
} }
@Override
public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) {
}
@Override @Override
public void installPeerActions(PeerActionsRegistrar registrar) { public void installPeerActions(PeerActionsRegistrar registrar) {
registrar.register(PeerAction.of( registrar.register(PeerAction.of(
@@ -1,52 +0,0 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package pitiupi.screenCast.GUI;
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 MulticastSubMenu {
private final ScreenCastShowMenu showMenu;
public ScreenCastMenu(ScreenCastPanel screenCastPanel) {
this.showMenu = new ScreenCastShowMenu(screenCastPanel);
}
@Override
public String getText() {
return "Screen Cast";
}
@Override
public List<MulticastMenu> getMenus() {
return List.of(showMenu);
}
private static final class ScreenCastShowMenu extends MulticastActionMenu {
private final ScreenCastPanel screenCastPanel;
ScreenCastShowMenu(ScreenCastPanel screenCastPanel) {
this.screenCastPanel = screenCastPanel;
}
@Override
public String getText() {
return "ScreenCast Window";
}
@Override
public void onClick() {
screenCastPanel.requestFocusInWindow();
}
}
}
@@ -9,14 +9,11 @@ import pitiupi.application.presence.UserPeerView;
import pitiupi.application.peeractions.PeerActionTarget; import pitiupi.application.peeractions.PeerActionTarget;
import pitiupi.application.peeractions.PeerTarget; import pitiupi.application.peeractions.PeerTarget;
import pitiupi.control.ConnectionManager; import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.models.Menus.MenuCategory;
import pitiupi.application.session.SessionNoticeChannel; import pitiupi.application.session.SessionNoticeChannel;
import pitiupi.application.session.SessionNoticePhase; import pitiupi.application.session.SessionNoticePhase;
import pitiupi.models.Messages.Message; import pitiupi.models.Messages.Message;
import pitiupi.models.Messages.SessionNoticeMessage; import pitiupi.models.Messages.SessionNoticeMessage;
import pitiupi.models.Plugin; import pitiupi.models.Plugin;
import pitiupi.screenCast.GUI.ScreenCastMenu;
import pitiupi.screenCast.GUI.ScreenCastPanel; import pitiupi.screenCast.GUI.ScreenCastPanel;
import pitiupi.screenCast.GUI.ScreenCastReceiveScope; import pitiupi.screenCast.GUI.ScreenCastReceiveScope;
import pitiupi.screenCast.net.ScreenCastMessage; import pitiupi.screenCast.net.ScreenCastMessage;
@@ -62,12 +59,6 @@ public class ScreenCastPlugin extends Plugin {
installPeerActions(workspace.peerActions()); installPeerActions(workspace.peerActions());
} }
@Override
public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) {
this.screenCastPanel = new ScreenCastPanel(connectionManager);
guiManager.createMenu(MenuCategory.Streaming, new ScreenCastMenu(screenCastPanel));
}
@Override @Override
public void onShellTabClosed() { public void onShellTabClosed() {
if (screenCastPanel != null) { if (screenCastPanel != null) {