Adiciona PluginStoreCli para smoke de instalação de JAR; reescreve o tutorial de ping e limitações didáticas; remove MainWindow/LegacyPluginWorkspace e move Sobre para AboutDialog na shell. Co-authored-by: Cursor <cursoragent@cursor.com>
4.6 KiB
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) |
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, GuiManager) |
Legado — adapter na carga; não usar em plugins novos |
getMessages() |
Lista de classes Message que o plugin desserializa |
receiveMessage(Message) |
Processamento após o PluginManager identificar o tipo |
Métodos opcionais (ciclo de vida e TCP avançado):
| Método | Função |
|---|---|
onShellTabClosed() |
Usuário fechou a aba do plugin no topo; liberar timers, captura, envio contínuo |
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.
O catálogo de build (plugins.registry.json) cobre só chat e screencast no CI. Exercícios (ex. ping) ficam fora do repositório — o aluno instala o JAR localmente após ant jar.
Carregamento de plugins
O PluginManager.loadPlugins() (chamado pela MainShellFrame):
- Lista arquivos
.jaremPITIUPI_HOME/plugins(ver 00_quickstart.md; symlinks de dev via./scripts/pitiupi.sh link). - Para cada JAR, cria um
URLClassLoadere usaServiceLoader.load(Plugin.class, classLoader). - Chama
createPlugin(connectionManager, pluginWorkspace)em cada instância (ShellPluginWorkspace). - Na recepção UDP/TCP, tenta desserializar com
PluginObjectInputStreame despacha para plugins que declararam o tipo emgetMessages().
Ao fechar uma aba dinâmica de plugin, TabWorkspacePanel notifica PluginManager.notifyShellTabClosed(pluginId) antes de remover o conteúdo, para o plugin encerrar trabalho em segundo plano.
Implementação: pitiupi/src/pitiupi/control/plugins/PluginManager.java.
Shell e abas (PluginWorkspace)
Na MainShellFrame, createPlugin(ConnectionManager, PluginWorkspace) registra o painel com bindShellWorkspaceContent(JPanel) sem abrir aba no boot. A galeria Plugins e as peer actions abrem a aba sob demanda.
Peer actions na lista lateral
PeerActionBroker.getAllActionsFor(target) agrupa ações de todos os plugins (submenu por getDisplayName()). Destinos: SINGLE_PEER (um usuário) ou ALL_PEERS (linha Todos).
Envio didático (núcleo)
PluginRoutingPanel (núcleo) padroniza destino, forma de envio e dica embaixo para chat, screencast e futuros plugins.
MessageDispatcher envia com TransportMode e SendTarget. PluginTransportCatalog define quais transportes cada plugin mostra na UI (sala geral vs um IP). Os combos usam textos didáticos (describeForUi) com o termo técnico entre parênteses (multicast UDP, UDP unicast, TCP). Na sala geral o campo IP fica desativado; um resumo “Como esta mensagem vai” (chat) deixa explícito o transporte escolhido — na sala pode ser multicast ou cópias unicast/TCP para cada pessoa, conforme o plugin. Para um peer use o IP manualmente ou a peer action na lista lateral.
Em abas de recebimento (ex. screencast view:), PluginRoutingPanel.bindReceiveSession deixa IP e forma de envio somente leitura, refletindo o que o remetente usou.
Avisos de sessão (D24)
SessionNoticeMessage (núcleo) sinaliza fases START / END por pluginId e canal (SessionNoticeChannel, ex. screen_cast). Plugins registram o tipo em getMessages() e reagem na UI (ex. fechar aba de recebimento ao END de compartilhamento). Campos incluem remetente, escopo (broadcast/direto) e TransportMode observado.