diff --git a/criar-plugin.md b/criar-plugin.md new file mode 100644 index 0000000..3b541a9 --- /dev/null +++ b/criar-plugin.md @@ -0,0 +1,256 @@ +# Como criar um plugin para o pitiupi ? + +O objetivo aqui, além de criar plugins para esse sistema, é entender **por que cada parte existe**. Isso é importante porque a ideia de **carregar código externo em tempo de execução** aparece em várias ferramentas nas áreas da computação. + +--- + + +## 1. Visão rápida + +O repositório contém projetos Java **independentes**: + +| Projeto | O que é | +|---|---| +| `pitiupi` | A ferramenta principal — P2P, janela, rede, e o "motor" que carrega plugins | +| `pitiupi.chat` | Um plugin que adiciona um chat | +| `pitiupi.screenCast` | Outro plugin que adiciona compartilhamento de tela | + +Cada plugin é compilado separadamente com `ant` e o `.jar` gerado é colocado (ou linkado) dentro da pasta `pitiupi/plugins`. + +Quando o pitiupi principal inicia, ele varre essa pasta e **carrega dinamicamente** cada plugin encontrado, sem que o código principal precise conhecer o plugin em tempo de compilação. + + +Isso é possível graças a três partes que trabalham juntas: + +1. A **interface `Plugin`** — o contrato que todo plugin deve cumprir. +2. O **`PluginLoader`** — a classe do pitiupi que varre a pasta `plugins/` e carrega os `.jar`. +3. O **arquivo de registro do `ServiceLoader`** — um arquivo de texto dentro do `.jar` que diz "esta classe aqui implementa `Plugin`". + +--- + +## 2. O contrato: a interface `Plugin` + +Todo plugin **precisa** implementar esta interface (`pitiupi/src/pitiupi/plugin/Plugin.java`): + +```java +public interface Plugin { + public String getName(); + public String getAuthor(); + public String getVersion(); + public void createPlugin(MainWindow window); + public Message getMessage(); + public void receiveMessage(byte[] message); +} +``` + +O que cada método faz: + +- **`getName()` / `getAuthor()` / `getVersion()`** — indentificação de dados básicos. +- **`createPlugin(MainWindow window)`** — É aqui que o plugin cria sua interface gráfica, adiciona itens de menu na janela principal, etc. +- **`getMessage()`** — Serve como modelo para a camada de rede saber que tipo de mensagem esse plugin envia. +- **`receiveMessage(byte[] message)`** — recebe as mensagens que chegam pela rede e permite que o plugin interprete esses dados. + +--- + +## 3. Como o pitiupi encontra e carrega os plugins + +A classe `PluginLoader` (`pitiupi/src/pitiupi/control/PluginLoader.java`) faz o seguinte: + +1. Olha dentro da pasta `plugins/` e lista todos os arquivos `.jar`. +2. Cria um `URLClassLoader` — um "carregador de classes" isolado, que sabe ler código de dentro desses `.jar`. +3. Usa `ServiceLoader.load(Plugin.class, pluginClassLoader)` para perguntar: *"dentre essas classes, quais implementam `Plugin`?"* +4. Para cada plugin encontrado, chama `createPlugin(mainWindow)` e guarda o plugin numa lista. +```java +ServiceLoader serviceLoader = ServiceLoader.load(Plugin.class, pluginClassLoader); + +for (Plugin plugin : serviceLoader) { + plugin.createPlugin(this.mainWindow); + this.mainWindow.getPlugins().add(plugin); +} +``` + +**Obs:** o `ServiceLoader` do Java **não escaneia o bytecode procurando quem implementa a interface**. Ele lê um arquivo de texto simples dentro do `.jar`. Se esse arquivo não existir ou estiver errado, o plugin é ignorado — mesmo que o código esteja perfeito. + +--- + +## 4.`META-INF/services` + +Dentro do projeto `pitiupi.chat` por exemplo, existe este arquivo: + +``` +pitiupi.chat/src/META-INF/services/pitiupi.plugin.Plugin +``` + +Repare em duas coisas: + +- A **pasta** se chama `META-INF/services`. +- O **nome do arquivo** é o nome completo da interface: `pitiupi.plugin.Plugin`. +- O **conteúdo** do arquivo é uma linha com o nome completo da classe que implementa essa interface: +``` +pitiupi.chat.plugin.ChatPlugin +``` + + Implementar `Plugin` diz "eu sei fazer o trabalho"; o arquivo em `META-INF/services` diz "aqui está quem sabe fazer o trabalho". Sem o segundo, o pitiupi nunca fica sabendo que a classe existe. +--- + +## 5. Exemplo real: o `ChatPlugin` + +Veja como o plugin de chat implementa o contrato — use isso como referência ao escrever o seu: + +```java +public class ChatPlugin implements Plugin { + + private ChatWindow chatWindow; + private MainWindow mainWindow; + + @Override + public void createPlugin(MainWindow mainWindow) { + this.mainWindow = mainWindow; + this.chatWindow = new ChatWindow(mainWindow); + this.mainWindow.addMenu(new ChatMenu(chatWindow)); + } + + @Override + public void receiveMessage(byte[] message) { + // desserializa os bytes recebidos de volta em um objeto ChatMessage + ByteArrayInputStream bis = new ByteArrayInputStream(message); + try { + ObjectInputStream ois = new ObjectInputStream(bis); + if (ois.readObject() instanceof ChatMessage msg) { + this.chatWindow.receiveChatMessage(msg); + } + } catch (IOException | ClassNotFoundException ex) { + System.out.println(ex.toString()); + } + } + + @Override + public String getName() { return "Chat"; } + + @Override + public String getAuthor() { return "Flávio, Tony e Gustavo "; } + + @Override + public String getVersion() { return "0"; } + + @Override + public Message getMessage() { return new ChatMessage(); } +} +``` + +Em `createPlugin` é onde o bum de "aparecer na tela" acontece: ele cria a janela do plugin e adiciona um menu na janela principal do pitiupi. + +--- + +## 6. Passo a passo: criando um plugin novo do zero + +Vamos chamar o plugin de exemplo de `SeuPlugin`. + +### Passo 1 — Criar a estrutura do projeto + +Se você quiser um jeito mais facil sem se aprofundar, você pode criar o projeto de duas formas: + +- **Mais simples:** copie um projeto Ant existente, como `pitiupi.chat` por exemplo, e remova o código específico do Chat. +- **Outra opção:** crie um novo projeto no NetBeans usando **"Java Class Library"**, mantendo a mesma estrutura de pastas. + + +``` +pitiupi.seuplugin/ +├── build.xml +├── nbproject/ +└── src/ + ├── META-INF/services/ + └── pitiupi/seuplugin/plugin/ +``` + +### Passo 2 — Adicionar o `pitiupi.jar` como dependência + +O plugin precisa do arquivo **`pitiupi/dist/pitiupi.jar`** para acessar `Plugin` e `MainWindow`. + +- **Se criou do zero:** configure o `pitiupi.jar` como dependência. +- **Se copiou um projeto existente:** essa configuração já está pronta. +- **Se criou pelo NetBeans:** **Properties → Libraries → Add JAR/Folder** → selecione `pitiupi/dist/pitiupi.jar`. + + +### Passo 3 — Implementar a interface `Plugin` + +Crie `src/pitiupi/seuplugin/plugin/SeuPlugin.java`: + +```java +package pitiupi.seuplugin.plugin; + +import pitiupi.GUI.MainWindow; +import pitiupi.net.Message; +import pitiupi.plugin.Plugin; + +public class SeuPlugin implements Plugin { + + @Override + public void createPlugin(MainWindow window) { + System.out.println("SeuPlugin foi carregado!"); + // aqui você criaria sua janela/menu, como o ChatPlugin faz + } + + @Override + public void receiveMessage(byte[] message) { + // trate as mensagens recebidas da rede aqui + } + + @Override + public String getName() { return "MeuPlugin"; } + + @Override + public String getAuthor() { return "Seu Nome"; } + + @Override + public String getVersion() { return "0.1"; } + + @Override + public Message getMessage() { return null; } // troque por uma Message real, se tiver +} +``` + +### Passo 4 — Registrar o plugin no `ServiceLoader` + +Crie o arquivo (sem extensão): + +``` +src/META-INF/services/pitiupi.plugin.Plugin +``` + +Com **uma única linha** dentro, o nome completo da sua classe: + +``` +pitiupi.seuplugin.plugin.MeuPlugin +``` + + +### Passo 5 — Compilar com Ant + +```bash +cd pitiupi.seuplugin +ant jar +``` + +Isso gera o `.jar` em `pitiupi.seuplugin/dist/`. + +### Passo 6 — Linkar o `.jar` na pasta de plugins + +```bash +cd pitiupi/plugins +ln -s ../../pitiupi.meuplugin/dist/*.jar . +``` + +Usar link simbólico (`ln -s`) é melhor durante o desenvolvimento: toda vez que você recompilar o plugin, não precisa copiar de novo. + + +## 7. Geral + +``` +Plugin.java → "o que todo plugin precisa saber fazer" (o contrato) +MeuPlugin.java → "como EU faço esse trabalho" (a implementação) +META-INF/services/pitiupi.plugin.Plugin → "aqui está quem faz o trabalho" (o registro) +PluginLoader.java → "eu procuro registros e chamo quem se registrou" (o carregador, já pronto no pitiupi) +``` + +Entendendo essas quatro peças, criar qualquer plugin novo — chat, screencast, ou algo totalmente diferente (arquivos, jogos, sensores) — é sempre o mesmo padrão: implementar a interface, registrar no `META-INF/services`, compilar e linkar. \ No newline at end of file