Author SHA1 Message Date
GustavoHMDS ab3840e4a4 correção de bugs 2026-10-09 13:51:06 -03:00
GustavoHMDS e25d3a425d WIP: Refatoração pronta, falta testes 2026-10-09 03:18:04 -03:00
Gustavo a628490fcb atualizar tutorial de plugins 2026-10-07 12:04:10 -03:00
Gustavo 395363e4bd Atualizei o tutorial de criação de plugins 2026-10-06 12:24:47 -03:00
GustavoHMDS 12497594e4 Correcao de bug e comeco da documentacao 2026-10-05 15:03:24 -03:00
tonyhcj 6a597092fe fix: corrige chaves desencontradas em GuiManager.buildMenu
Um bloco do if (menu instanceof UnicastSubMenu) estava duplicado e
incompleto (sobrava um fragmento com reticências soltas), o que
impedia a compilação do núcleo. Remove a duplicação, mantendo só
o bloco correto e completo que já existia logo depois.
2026-10-03 20:11:17 -03:00
tonyhcj ffd474e7ff docs: atualiza README 2026-10-03 16:55:08 -03:00
tonyhcj 4d39648ee7 docs: atualiza README 2026-10-03 16:51:29 -03:00
tonyhcj 095af14463 fix: remove build/ do controle de versão 2026-10-03 16:32:26 -03:00
tonyhcj d0083e8069 refactor: ChatPlugin usa Pitiupi 2026-10-03 16:10:10 -03:00
tonyhcj dda324228f refactor: ChatWindow usa Pitiupi 2026-10-03 16:10:05 -03:00
tonyhcj 840183ee9f refactor: injeta Pitiupi nos plugins 2026-10-03 16:09:55 -03:00
tonyhcj eb3ce132cd refactor: Plugin usa fachada Pitiupi 2026-10-03 16:09:49 -03:00
tonyhcj d1dc427185 feat: cria fachada Pitiupi 2026-10-03 16:09:44 -03:00
GustavoHMDS 7c9e61ce0f Novos menus e envio de mensagens 2026-10-02 20:29:35 -03:00
GustavoHMDS 6538dac8af Mover algumas classes para a camada de modelo 2026-10-01 22:21:19 -03:00
Gustavo 67813e4881 bug fix de erro na compilacao 2026-10-01 10:42:49 -03:00
gustavo 4cc07a3754 Merge pull request 'feature/separacao-gerenciadores' (#1) from feature/separacao-gerenciadores into main
Reviewed-on: #1
2026-09-30 18:34:53 -03:00
GustavoHMDS fa696194aa Plugins adaptados para a nova versão 2026-09-30 13:58:35 -03:00
GustavoHMDS 9fb4952f32 Atualizei os plugins para funcionarem com a nova divisão 2026-09-29 11:30:48 -03:00
GustavoHMDS 96b8faca5b WIP bugfix 2026-09-29 03:05:21 -03:00
GustavoHMDS cac549f1ee WIP bugfix 2026-09-29 03:04:56 -03:00
GustavoHMDS a86f4dbf82 correcao conflitos 2026-09-29 02:08:54 -03:00
GustavoHMDS 23559dcc06 WIP bug fix 3 2026-09-29 01:20:48 -03:00
GustavoHMDS fa4ee046b1 WIP BugFix 3 2026-09-29 01:13:42 -03:00
GustavoHMDS 0c71a85cba WIP bug fix 3 2026-09-29 00:21:04 -03:00
GustavoHMDS e7c8983775 WIP TCP bug fix 2 2026-09-28 22:05:14 -03:00
GustavoHMDS 0ec3129a22 WIP TCP bug fix 2026-09-28 21:43:17 -03:00
GustavoHMDS ba2f41daf7 WIP bug fix 2026-09-28 18:59:20 -03:00
GustavoHMDS 57059c3ef0 WIP bugfix 2026-09-28 12:42:49 -03:00
GustavoHMDS 7168431bcd WIP 2026-09-28 10:33:09 -03:00
tony 0b7c67fef6 Excluir fg 2026-09-24 05:00:29 -03:00
tony 15ea8ac207 Adicionar fg 2026-09-24 04:59:49 -03:00
GustavoHMDS b5bbd1df2d atualizar javadoc dos das classes SocketTCP, ConnectionManager, ConnectionFactory e TcpConnection 2026-09-09 00:14:15 -03:00
GustavoHMDS 93ac292f72 bugfix: SocketUDP converte para Mensagem antes de enviar ao plugin 2026-09-08 01:37:03 -03:00
GustavoHMDS b610ec20e1 Esqueci de adicionar nos commits anteriores 2026-09-08 01:30:23 -03:00
GustavoHMDS 4461772ebf modificar o socketTCP para servir de interface com o restante da aplicação, enquanto usa a factory e o manager para criar conexões conforme necessário 2026-09-08 01:29:23 -03:00
GustavoHMDS 0d5a4dbe75 adicionar um gerenciador de conexões que mantem um registro das conexões ativas e cuida do seu encerramento 2026-09-08 01:25:52 -03:00
GustavoHMDS bbb1ceea23 adicionar suporte para criação de conexões privadas, com uma interface para plugins que queiram usa-los e uma fabrica que faz a validação das conexões 2026-09-08 01:24:35 -03:00
GustavoHMDS 7d9ca4ca71 adicionar enum utilizado para o commit anterior 2026-09-08 01:21:36 -03:00
GustavoHMDS 1fcc03d9be adicionar classe para envio de mensagens do sistema 2026-09-08 01:20:34 -03:00
GustavoHMDS 3efc59444f impedindo TCP para si mesmo 2026-08-18 12:53:30 -03:00
GustavoHMDS 947aaf00b7 Adicionando o plugin corretamente 2026-08-18 10:06:47 -03:00
GustavoHMDS e9ce17abb3 Plugin TesteSocket para testar as mudanças 2026-08-18 09:51:22 -03:00
GustavoHMDS 45e3bf6876 Unicast implementado, mas falta testes 2026-08-17 14:11:09 -03:00
GustavoHMDS 81c37225fa atualizando o arquivo documentation.md 2026-08-11 14:35:51 -03:00
GustavoHMDS 5c69189293 atualizando documentacao depois do merge 2026-08-11 14:30:17 -03:00
GustavoHMDS 543073e84e resolvendo conflitos 2026-08-11 10:12:04 -03:00
tonyhcj 7f21e89a0a fix: salvando nome e porta 2026-07-29 15:00:44 -03:00
tonyhcj 239a10e79f fix: Corrige bug que exibia frame0 como nome do usuário 2026-07-28 22:11:37 -03:00
tonyhcj a7ba307daf fix: usar instanceId único em vez de userName para identificar heartbeats 2026-07-24 19:31:39 -03:00
GustavoHMDS cf43f62793 Documentação de funções e arquivo Documentação.md 2026-07-21 20:33:28 -03:00
GustavoHMDS 5d650df8eb Merge branch 'feature/heartbeat-online-users' of https://git.alice.ufsj.edu.br/fls/pitiupi into feature/help-and-documentation 2026-07-21 11:09:23 -03:00
GustavoHMDS 3beadca0c1 comeco da documentacao 2026-07-21 11:06:23 -03:00
139 changed files with 4264 additions and 595 deletions

No files matched your search

+3 -7
View File
@@ -1,7 +1,3 @@
/pitiupi/build/ build/
/pitiupi/dist/ dist/
/pitiupi.chat/build/ *.class
/pitiupi.chat/dist/
/pitiupi.chat/nbproject/private/
/pitiupi.screenCast/build/
/pitiupi.screenCast/dist/
+10
View File
@@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+9
View File
@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="pitiupi">
<CLASSES>
<root url="jar://$PROJECT_DIR$/pitiupi.screenCast/dist/lib/pitiupi.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+9
View File
@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="pitiupi.chat">
<CLASSES>
<root url="jar://$PROJECT_DIR$/pitiupi.chat/dist/pitiupi.chat.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+9
View File
@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="pitiupi.screenCast">
<CLASSES>
<root url="jar://$PROJECT_DIR$/pitiupi.screenCast/dist/pitiupi.screenCast.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+10
View File
@@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="plugins">
<CLASSES>
<root url="jar://$PROJECT_DIR$/pitiupi/plugins/pitiupi.screenCast.jar!/" />
<root url="jar://$PROJECT_DIR$/pitiupi/plugins/pitiupi.chat.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+3
View File
@@ -0,0 +1,3 @@
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="openjdk-27" project-jdk-type="JavaSDK" />
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/pitiupi.iml" filepath="$PROJECT_DIR$/.idea/pitiupi.iml" />
</modules>
</component>
</project>
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
Generated
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/udpTest" vcs="Git" />
</component>
</project>
+23 -3
View File
@@ -1,10 +1,13 @@
# pitiupi # pitiupi
Ferramenta p2p para práticas em rede Este repositório tem 4 projetos Java que são independentes:
Este repositório tem 3 projetos Java que são independentes: ### Sistema:
- A ferramenta pitiupi (P2P) - A ferramenta pitiupi (P2P)
### Plugins:
- Um plugin de Chat - Um plugin de Chat
- Um plugin de Chat multiprotocolo (TCP/UDP)
- Um plugin de ScreenCast - Um plugin de ScreenCast
Cada um destes projetos deve ser compilado individualmente. Para isso, use a ferramenta ant no diretório do projeto. Cada um destes projetos deve ser compilado individualmente. Para isso, use a ferramenta ant no diretório do projeto.
@@ -21,9 +24,26 @@ Para criar links simbólicos no Linux, use o comando:
``` ```
cd pitiupi/plugins cd pitiupi/plugins
ln -s ../../pitiupi.chat/dist/*.jar . ln -s ../../pitiupi.chat/dist/*.jar .
ln -s ../../pitiupi.mpChat/dist/*.jar .
ln -s ../../pitiupi.screenCast/dist/*.jar . ln -s ../../pitiupi.screenCast/dist/*.jar .
``` ```
Isso deve resolver! Isso deve resolver!
Para criar um novo plugin: ## Como um plugin conversa com o pitiupi
Um plugin nunca recebe acesso direto às classes internas do núcleo (ConnectionManager, GuiManager). Em vez disso, recebe uma facade, a classe Pitiupi, que expõe só o que um plugin precisa: enviar mensagem (multicast, unicast ou tcp), abrir conexão tcp privada, ler nome de usuário e porta, criar menu.
```java
public abstract void createPlugin(Pitiupi pitiupi);
```
## Para criar um novo plugin
1. Implemente `pitiupi.models.Plugin`, incluindo `createPlugin(Pitiupi pitiupi)`
2. Registre a classe em `src/META-INF/services/pitiupi.models.Plugin`, com o nome completo da classe numa linha
3. Compile com `ant jar`
4. Linke o `.jar` em `pitiupi/plugins`
Use `pitiupi.chat` como referência — é o exemplo mais simples e completo.
Plugins que ainda não foram atualizados para a Pitiupi (como uma versão mais antiga do screenCast) não compilam contra o núcleo atual e precisam de migração antes de serem usados.
+5
View File
@@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
+9
View File
@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="pitiupi.chat">
<CLASSES>
<root url="jar://$PROJECT_DIR$/dist/pitiupi.chat.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+5
View File
@@ -0,0 +1,5 @@
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="openjdk-26" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/pitiupi.chat.iml" filepath="$PROJECT_DIR$/pitiupi.chat.iml" />
</modules>
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>
-6
View File
@@ -1,6 +0,0 @@
#Thu, 16 Jul 2026 15:21:36 -0300
/home/flavio/NetBeansProjects/pitiupi.chat=
/home/flavio/NetBeansProjects/pitiupi=
Binary file not shown.
+2 -2
View File
@@ -47,8 +47,8 @@ javac.modulepath=
javac.processormodulepath= javac.processormodulepath=
javac.processorpath=\ javac.processorpath=\
${javac.classpath} ${javac.classpath}
javac.source=25 javac.source=17
javac.target=25 javac.target=17
javac.test.classpath=\ javac.test.classpath=\
${javac.classpath}:\ ${javac.classpath}:\
${build.classes.dir} ${build.classes.dir}
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../pitiupi/dist/pitiupi.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>
@@ -1 +0,0 @@
pitiupi.chat.plugin.ChatPlugin
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../../pitiupi/dist/pitiupi.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+15 -14
View File
@@ -4,28 +4,29 @@
*/ */
package pitiupi.chat.GUI; package pitiupi.chat.GUI;
import java.awt.event.ActionEvent; import pitiupi.models.Menus.MulticastMenus.MulticastMenu;
import javax.swing.JMenu; import pitiupi.models.Menus.MulticastMenus.MulticastSubMenu;
import java.util.List;
/** /**
* *
* @author flavio * @author flavio
*/ */
public class ChatMenu extends JMenu { public class ChatMenu extends MulticastSubMenu {
ChatMulticastMenu chatMulticastMenu;
private final ChatWindow chatWindow;
private javax.swing.JMenuItem menuItem;
public ChatMenu(ChatWindow chatWindow) { public ChatMenu(ChatWindow chatWindow) {
this.chatWindow = chatWindow; this.chatMulticastMenu = new ChatMulticastMenu(chatWindow);
this.setText("Chat");
menuItem = new javax.swing.JMenuItem();
menuItem.setText("Show Chat Window");
menuItem.addActionListener(this::chatMenuActionPerformed);
this.add(menuItem);
} }
private void chatMenuActionPerformed(ActionEvent ae) { @Override
this.chatWindow.setVisible(true); public String getText() {
return "Chat";
}
@Override
public List<MulticastMenu> getMenus() {
return List.of(chatMulticastMenu);
} }
} }
@@ -0,0 +1,21 @@
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);
}
}
@@ -5,37 +5,35 @@
*/ */
package pitiupi.chat.GUI; package pitiupi.chat.GUI;
import java.io.IOException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import pitiupi.chat.net.ChatMessage; import pitiupi.chat.net.ChatMessage;
import pitiupi.GUI.MainWindow; import pitiupi.plugin.Pitiupi;
import pitiupi.net.Message; import pitiupi.models.Messages.Message;
import javax.swing.*;
/** /**
* * @author tony
* @author flavio * @author flavio
*/ */
public class ChatWindow extends javax.swing.JDialog { public class ChatWindow extends JDialog {
private javax.swing.JButton jButton1; private JButton jButton1;
private javax.swing.JEditorPane jEditorPane1; private JEditorPane jEditorPane1;
private javax.swing.JPanel jPanel1; private JPanel jPanel1;
private javax.swing.JPanel jPanel2; private JPanel jPanel2;
private javax.swing.JScrollPane jScrollPane1; private JScrollPane jScrollPane1;
private javax.swing.JScrollPane jScrollPane2; private JScrollPane jScrollPane2;
private javax.swing.JSplitPane jSplitPane1; private JSplitPane jSplitPane1;
private javax.swing.JTextArea jTextArea1; private JTextArea jTextArea1;
private final MainWindow mainWindow; private final Pitiupi pitiupi;
public ChatWindow(MainWindow mainWindow) {
this.mainWindow = mainWindow;
initComponents();
}
public ChatWindow(Pitiupi pitiupi) {
this.pitiupi = pitiupi;
initComponents();
}
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always * WARNING: Do NOT modify this code. The content of this method is always
@@ -43,23 +41,23 @@ public class ChatWindow extends javax.swing.JDialog {
*/ */
private void initComponents() { private void initComponents() {
this.setDefaultCloseOperation(javax.swing.WindowConstants.HIDE_ON_CLOSE); this.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
this.setTitle("Chat Window"); this.setTitle("Chat Window");
this.setBounds(new java.awt.Rectangle(0, 0, 800, 600)); this.setBounds(new java.awt.Rectangle(0, 0, 800, 600));
this.getContentPane().setLayout(new java.awt.BorderLayout(5, 5)); this.getContentPane().setLayout(new java.awt.BorderLayout(5, 5));
this.setPreferredSize(new java.awt.Dimension(800, 600)); this.setPreferredSize(new java.awt.Dimension(800, 600));
jSplitPane1 = new javax.swing.JSplitPane(); jSplitPane1 = new JSplitPane();
jScrollPane1 = new javax.swing.JScrollPane(); jScrollPane1 = new JScrollPane();
jEditorPane1 = new javax.swing.JEditorPane(); jEditorPane1 = new JEditorPane();
jPanel1 = new javax.swing.JPanel(); jPanel1 = new JPanel();
jScrollPane2 = new javax.swing.JScrollPane(); jScrollPane2 = new JScrollPane();
jTextArea1 = new javax.swing.JTextArea(); jTextArea1 = new JTextArea();
jPanel2 = new javax.swing.JPanel(); jPanel2 = new JPanel();
jButton1 = new javax.swing.JButton(); jButton1 = new JButton();
jSplitPane1.setDividerLocation(500); jSplitPane1.setDividerLocation(500);
jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); jSplitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT);
jEditorPane1.setContentType("text/html"); jEditorPane1.setContentType("text/html");
jPanel1.setLayout(new java.awt.BorderLayout()); jPanel1.setLayout(new java.awt.BorderLayout());
jTextArea1.setColumns(20); jTextArea1.setColumns(20);
@@ -91,20 +89,16 @@ public class ChatWindow extends javax.swing.JDialog {
} }
private void sendMessage() { private void sendMessage() {
if (jTextArea1.getText().trim().length() == 0) { if (jTextArea1.getText().trim().isEmpty()) {
return; return;
} }
ChatMessage message = new ChatMessage(mainWindow.getName(), jTextArea1.getText()); ChatMessage message = new ChatMessage(pitiupi.getUserName(), jTextArea1.getText());
this.sendChatMessage(message); this.sendChatMessage(message);
jTextArea1.setText(""); jTextArea1.setText("");
} }
public void sendChatMessage(Message message) { public void sendChatMessage(Message message) {
try { pitiupi.sendUdpMulticast(message);
mainWindow.getSocket().send(message.toByteArray());
} catch (IOException ex) {
Logger.getLogger(MainWindow.class.getName()).log(Level.SEVERE, null, ex);
}
} }
public void receiveChatMessage(ChatMessage message) { public void receiveChatMessage(ChatMessage message) {
@@ -5,14 +5,14 @@
*/ */
package pitiupi.chat.net; package pitiupi.chat.net;
import pitiupi.net.Message;
import pitiupi.net.Message; import pitiupi.models.Messages.Message;
/** /**
* *
* @author flavio * @author flavio
*/ */
public class ChatMessage extends Message{ public class ChatMessage extends Message {
private String text; private String text;
private String name; private String name;
@@ -4,48 +4,43 @@
*/ */
package pitiupi.chat.plugin; package pitiupi.chat.plugin;
import java.io.ByteArrayInputStream; import java.util.ArrayList;
import java.io.IOException; import java.util.Collection;
import java.io.ObjectInputStream; import java.util.List;
import pitiupi.GUI.MainWindow;
import pitiupi.plugin.Pitiupi;
import pitiupi.chat.GUI.ChatMenu; import pitiupi.chat.GUI.ChatMenu;
import pitiupi.chat.GUI.ChatWindow; import pitiupi.chat.GUI.ChatWindow;
import pitiupi.chat.net.ChatMessage; import pitiupi.chat.net.ChatMessage;
import pitiupi.net.Message; import pitiupi.models.Menus.MenuCategory;
import pitiupi.plugin.Plugin; import pitiupi.models.Messages.Message;
import pitiupi.models.Plugin;
/** /**
* *@author tony
* @author flavio * @author flavio
*/ */
public class ChatPlugin implements Plugin { public class ChatPlugin extends Plugin {
private ChatWindow chatWindow; private ChatWindow chatWindow;
private MainWindow mainWindow;
public ChatPlugin() { @Override
public void createPlugin(Pitiupi pitiupi) {
this.chatWindow = new ChatWindow(pitiupi);
pitiupi.createMenu(MenuCategory.Social, new ChatMenu(chatWindow));
} }
@Override
public void createPlugin(MainWindow mainWindow) {
this.mainWindow = mainWindow;
this.chatWindow = new ChatWindow(mainWindow);
this.mainWindow.addMenu(new ChatMenu(chatWindow));
}
@Override @Override
public void receiveMessage(byte[] message) { public List<Class<? extends Message>> getMessages() {
ByteArrayInputStream bis = new ByteArrayInputStream(message); return List.of(ChatMessage.class);
ObjectInputStream ois; }
try {
ois = new ObjectInputStream(bis);
if (ois.readObject() instanceof ChatMessage msg) { @Override
this.chatWindow.receiveChatMessage(msg); public void receiveMessage(Message message) {
} if (message instanceof ChatMessage msg) {
} catch (IOException ex) { this.chatWindow.receiveChatMessage(msg);
System.out.println(ex.toString());
} catch (ClassNotFoundException ex) {
System.out.println(ex.toString());
} }
} }
@@ -65,7 +60,9 @@ public class ChatPlugin implements Plugin {
} }
@Override @Override
public Message getMessage() { public String getIdentifier() {
return new ChatMessage(); return "Pitiupi.Chat.Example";
} }
} }
+85
View File
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="e2cdacc6-69cd-4190-bef1-27719e32140a" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/../.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../.idea/vcs.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../.idea/vcs.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/GUI/PrivateChatWindow.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/GUI/PrivateChatWindow.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/GUI/PublicChatWindow.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/GUI/PublicChatWindow.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/plugin/MultiProtocolChat.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/plugin/MultiProtocolChat.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/build/built-jar.properties" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/build/built-jar.properties" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/build/classes/pitiupi/screenCast/GUI/ScreenCastMenu.class" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/build/classes/pitiupi/screenCast/GUI/ScreenCastMenu.class" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/build/classes/pitiupi/screenCast/GUI/ScreenCastWindow.class" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/build/classes/pitiupi/screenCast/GUI/ScreenCastWindow.class" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/build/classes/pitiupi/screenCast/net/ScreenCastMessage.class" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/build/classes/pitiupi/screenCast/net/ScreenCastMessage.class" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/build/classes/pitiupi/screenCast/plugin/ScreenCastPlugin.class" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/build/classes/pitiupi/screenCast/plugin/ScreenCastPlugin.class" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/src/pitiupi.screenCast.iml" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/src/pitiupi.screenCast.iml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastMenu.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastMenu.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastWindow.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/src/pitiupi/screenCast/GUI/ScreenCastWindow.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/src/pitiupi/screenCast/net/ScreenCastMessage.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/src/pitiupi/screenCast/net/ScreenCastMessage.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.screenCast/src/pitiupi/screenCast/plugin/ScreenCastPlugin.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.screenCast/src/pitiupi/screenCast/plugin/ScreenCastPlugin.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi/src/pitiupi/control/plugins/PluginManager.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/src/pitiupi/control/plugins/PluginManager.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi/src/pitiupi/net/tcp/SocketTCP.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/src/pitiupi/net/tcp/SocketTCP.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi/src/pitiupi/net/tcp/TcpConnection.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/src/pitiupi/net/tcp/TcpConnection.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Class" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 6
}</component>
<component name="ProjectId" id="3K7MYfvmfmI7sWODbOakShpPyuR" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;update/mpChat&quot;,
&quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;C:/Users/extre/OneDrive/Documentos/Faculdade/2026_02/Monitoria/Pitiupi/pitiupi.mpChat&quot;,
&quot;project.structure.last.edited&quot;: &quot;Modules&quot;,
&quot;project.structure.proportion&quot;: &quot;0.15&quot;,
&quot;project.structure.side.proportion&quot;: &quot;0.2&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.keymap&quot;
}
}</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-9823dce3aa75-bf35d07a577b-intellij.indexing.shared.core-IU-252.28539.97" />
</set>
</attachedChunks>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="e2cdacc6-69cd-4190-bef1-27719e32140a" name="Changes" comment="" />
<created>1790904201925</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1790904201925</updated>
</task>
<servers />
</component>
</project>
+16
View File
@@ -0,0 +1,16 @@
JAR = ../pitiupi/dist/pitiupi.jar
OUT_DIR = out
META-INF_DIR = src/META-INF
DIST = dist/mpChat.jar
SOURCES = $(shell find -name "*.java")
all:
mkdir -p "$(OUT_DIR)"
mkdir -p "$(dir $(DIST))"
javac -cp "$(JAR)" -d "$(OUT_DIR)" $(SOURCES)
cp -r "$(META-INF_DIR)" "$(OUT_DIR)"
jar cf "$(DIST)" -C "$(OUT_DIR)" .
clean:
rm -rf "$(OUT_DIR)"/*
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
plugin.MultiProtocolChat
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../pitiupi/dist/pitiupi.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>
+40
View File
@@ -0,0 +1,40 @@
package GUI;
import pitiupi.models.Menus.MenuCategory;
import pitiupi.models.Menus.MulticastMenus.MulticastSubMenu;
import plugin.MultiProtocolChat;
import javax.swing.*;
import java.awt.*;
public class ChatMenu extends MulticastSubMenu {
private final MultiProtocolChat multiProtocolChat;
public ChatMenu(MultiProtocolChat multiProtocolChat) {
this.multiProtocolChat = multiProtocolChat;
this.setText("MP-Chat");
JMenuItem startPrivateChat = new JMenuItem("start Private Chat");
startPrivateChat.addActionListener(e -> showPrivateChatDialog());
this.add(startPrivateChat);
}
public void newMenuItem(JMenuItem newMenuItem, ChatWindow chatWindow) {
newMenuItem.addActionListener(e -> chatWindow.setVisible(true));
this.add(newMenuItem);
}
public void removeMenuItem(JMenuItem removeMenuItem) {
this.remove(removeMenuItem);
}
private void showPrivateChatDialog() {
}
@Override
public String getText() {
return "MP-Chat";
}
}
+94
View File
@@ -0,0 +1,94 @@
package GUI;
import net.MpChatMessage;
import pitiupi.control.ConnectionManager;
import javax.swing.*;
import java.awt.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
public abstract class ChatWindow extends JFrame {
protected final ConnectionManager connectionManager;
private JTextArea receivedMessagesArea;
protected JTextField messageField;
public ChatWindow(ConnectionManager connectionManager) {
this.connectionManager = connectionManager;
initComponents();
}
private void initComponents() {
setTitle("Socket Test");
setSize(700, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
initReceivedMessagesPanel(mainPanel);
initBottomPanel(mainPanel);
setContentPane(mainPanel);
}
private void initReceivedMessagesPanel(JPanel mainPanel) {
receivedMessagesArea = new JTextArea();
receivedMessagesArea.setEditable(false);
receivedMessagesArea.setLineWrap(true);
receivedMessagesArea.setWrapStyleWord(true);
JScrollPane messagesScrollPane = new JScrollPane(receivedMessagesArea);
messagesScrollPane.setBorder(BorderFactory.createTitledBorder("Mensagens recebidas"));
mainPanel.add(messagesScrollPane, BorderLayout.CENTER);
}
private void initBottomPanel(JPanel mainPanel) {
messageField = new JTextField();
JPanel bottomPanel = new JPanel(new BorderLayout(10, 10));
bottomPanel.add(messageField, BorderLayout.CENTER);
initButtons(bottomPanel);
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
}
protected abstract void initButtons(JPanel bottomPanel);
protected MpChatMessage createMessage(String tipoMensagem) {
try {
String nome = connectionManager.getUserName();
String ip = InetAddress.getLocalHost().getHostAddress();
String message = messageField.getText();
return new MpChatMessage(nome, ip, tipoMensagem, message);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
public void receiveMessage(MpChatMessage msg) {
SwingUtilities.invokeLater(() -> {
String mensagem = String.format(
"[%s] %s (%s): %s%n",
msg.getTipoMensagem(),
msg.getNome(),
msg.getIp(),
msg.getMessage()
);
receivedMessagesArea.append(mensagem);
receivedMessagesArea.setCaretPosition(receivedMessagesArea.getDocument().getLength());
});
}
}
@@ -0,0 +1,38 @@
package GUI;
import control.ChatController;
import pitiupi.models.Menus.UnicastMenus.UnicastActionMenu;
import pitiupi.models.User;
import plugin.MultiProtocolChat;
import javax.swing.*;
import java.awt.*;
import java.net.InetAddress;
public class PrivateChatMenu extends UnicastActionMenu {
PrivateChatWindow privateChatWindow;
private final MultiProtocolChat multiProtocolChat;
private final ChatController chatController;
public PrivateChatMenu(PrivateChatWindow PrivateChatWindow, MultiProtocolChat multiProtocolChat, ChatController chatController) {
this.privateChatWindow = PrivateChatWindow;
this.multiProtocolChat = multiProtocolChat;
this.chatController = chatController;
}
@Override
public void onClick(User user) {
if (multiProtocolChat.openConnection(user.getAddress())
else errorLabel.setVisible(true);
dialog.setContentPane(contentPanel);
dialog.pack();
dialog.setLocationRelativeTo(SwingUtilities.getWindowAncestor(privateChatWindow));
dialog.setVisible(true);
}
@Override
public String getText() {
return "start Private Chat";
}
}
@@ -0,0 +1,43 @@
package GUI;
import net.MpChatMessage;
import pitiupi.control.ConnectionManager;
import pitiupi.net.tcp.TcpConnection;
import javax.swing.*;
import java.awt.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class PrivateChatWindow extends ChatWindow {
private final TcpConnection connection;
public PrivateChatWindow(ConnectionManager ConnectionManager, TcpConnection connection) {
super(ConnectionManager);
this.connection = connection;
}
@Override
protected void initButtons(JPanel bottomPanel) {
JButton tcpButton = new JButton("TCP");
tcpButton.addActionListener(e -> sendTCP());
JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
buttonsPanel.add(tcpButton);
bottomPanel.add(buttonsPanel, BorderLayout.EAST);
}
private void sendTCP() {
try {
MpChatMessage msg = createMessage("TCP Private");
connection.send(msg.toByteArray());
} catch (Exception ex) {
Logger.getLogger(PrivateChatWindow.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
@@ -0,0 +1,21 @@
package GUI;
import pitiupi.models.Menus.MulticastMenus.MulticastActionMenu;
public class PublicChatMenu extends MulticastActionMenu {
PublicChatWindow publicChatWindow;
PublicChatMenu(PublicChatWindow publicChatWindow) {
this.publicChatWindow = publicChatWindow;
}
@Override
public void onClick() {
publicChatWindow.setVisible(true);
}
@Override
public String getText() {
return "";
}
}
@@ -0,0 +1,93 @@
package GUI;
import net.MpChatMessage;
import pitiupi.control.ConnectionManager;
import javax.swing.*;
import java.awt.*;
import java.net.InetAddress;
import java.util.logging.Level;
import java.util.logging.Logger;
public class PublicChatWindow extends ChatWindow {
private JTextField ipField;
public PublicChatWindow(ConnectionManager connectionManager) {
super(connectionManager);
}
@Override
protected void initButtons(JPanel bottomPanel) {
initIpField(bottomPanel);
initCommunicationButtons(bottomPanel);
}
private void initIpField(JPanel bottomPanel) {
ipField = new JTextField();
ipField.setPreferredSize(new Dimension(130, 30));
JPanel ipPanel = new JPanel(new BorderLayout(5, 5));
ipPanel.add(new JLabel("IP destino:"), BorderLayout.WEST);
ipPanel.add(ipField, BorderLayout.CENTER);
bottomPanel.add(ipPanel, BorderLayout.WEST);
}
private void initCommunicationButtons(JPanel bottomPanel) {
JButton tcpButton = new JButton("TCP");
JButton udpButton = new JButton("UDP");
JButton multicastButton = new JButton("Multicast");
tcpButton.addActionListener(e -> sendTCP());
udpButton.addActionListener(e -> sendUDP());
multicastButton.addActionListener(e -> sendMulticast());
JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
buttonsPanel.add(tcpButton);
buttonsPanel.add(udpButton);
buttonsPanel.add(multicastButton);
bottomPanel.add(buttonsPanel, BorderLayout.EAST);
}
private void sendTCP() {
try {
MpChatMessage msg = createMessage("TCP Unicast");
InetAddress ip = InetAddress.getByName(ipField.getText());
connectionManager.sendTcp(msg, ip);
} catch (Exception ex) {
Logger.getLogger(PublicChatWindow.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void sendUDP() {
try {
MpChatMessage msg = createMessage("UDP Unicast");
InetAddress ip = InetAddress.getByName(ipField.getText());
connectionManager.sendUdpUnicast(msg, ip);
} catch (Exception ex) {
Logger.getLogger(PublicChatWindow.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void sendMulticast() {
try {
MpChatMessage msg = createMessage("UDP Multicast");
connectionManager.sendUdpMulticast(msg);
} catch (Exception ex) {
Logger.getLogger(PublicChatWindow.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
@@ -0,0 +1 @@
plugin.MultiProtocolChat
+35
View File
@@ -0,0 +1,35 @@
package net;
import pitiupi.models.Messages.Message;
public class MpChatMessage extends Message {
private String nome;
private String ip;
private String tipoMensagem;
private String message;
public MpChatMessage(String nome, String ip, String tipoMensagem, String message) {
this.nome = nome;
this.ip = ip;
this.tipoMensagem = tipoMensagem;
this.message = message;
}
public MpChatMessage(){}
public String getNome() {
return nome;
}
public String getIp() {
return ip;
}
public String getTipoMensagem() {
return tipoMensagem;
}
public String getMessage() {
return message;
}
}
@@ -0,0 +1,109 @@
package plugin;
import GUI.ChatMenu;
import control.ChatController;
import net.MpChatMessage;
import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.models.Messages.Message;
import pitiupi.net.tcp.TcpConnection;
import pitiupi.models.Plugin;
import javax.swing.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.InetAddress;
public class MultiProtocolChat extends Plugin {
ChatController chatController;
ConnectionManager connectionManager;
GuiManager guiManager;
@Override
public String getName() {
return "MultiProtocol chat";
}
@Override
public String getAuthor() {
return "Gustavo Henrique";
}
@Override
public String getVersion() {
return "0";
}
@Override
public String getIdentifier() {
return "Multi.Protocol.Chat.Pitiupi";
}
@Override
public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) {
this.connectionManager = connectionManager;
this.guiManager = guiManager;
ChatMenu chatMenu = new ChatMenu(this);
chatController = new ChatController(connectionManager, chatMenu);
guiManager.addMulticastMenu(chatMenu);
}
@Override
public Message getMessage() {
return new MpChatMessage();
}
@Override
public void receiveMessage(byte[] message) {
MpChatMessage mpChatMessage = parseMessage(message);
chatController.receivePublic(mpChatMessage);
}
@Override
public boolean receivePrivateConnection(TcpConnection connection) {
SwingUtilities.invokeLater(() -> {
chatController.addChatWindow(connection);
});
return true;
}
@Override
public boolean receiveBytes(TcpConnection connection, byte[] bytes) {
MpChatMessage mpChatMessage = parseMessage(bytes);
chatController.receivePrivate(connection, mpChatMessage);
return true;
}
@Override
public boolean onSocketClosed(TcpConnection connection) {
chatController.removeChatWindow(connection);
return true;
}
public boolean openConnection(String ip) {
try {
connectionManager.connectPluginTcp(InetAddress.getByName(ip), getIdentifier(), getIdentifier());
return true;
}
catch (Exception e) {
System.out.println("Error opening connection: " + e.getMessage());
}
return false;
}
private MpChatMessage parseMessage(byte[] bytes) {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois;
try {
ois = new ObjectInputStream(bis);
if (ois.readObject() instanceof MpChatMessage msg) {
return msg;
}
} catch (IOException | ClassNotFoundException ex) {
System.out.println(ex.toString());
}
return null;
}
}
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AntConfiguration">
<buildFile url="file://$PROJECT_DIR$/build.xml" />
</component>
</project>
+3
View File
@@ -0,0 +1,3 @@
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="openjdk-26" project-jdk-type="JavaSDK" />
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/pitiupi.screenCast.iml" filepath="$PROJECT_DIR$/.idea/pitiupi.screenCast.iml" />
</modules>
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>
+113
View File
@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="083e72ce-fa9b-4ddf-a0ef-24105aa3eabb" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/../.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../.idea/vcs.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../.idea/vcs.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 7
}</component>
<component name="ProjectId" id="3Gs0wFaRD4HyhP9VXLUWBeq0qTL" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
"codeWithMe.voiceChat.enabledByDefault": "false",
"git-widget-placeholder": "update/screenCast",
"ignore.virus.scanning.warn.message": "true",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "C:/Users/Gustavo/OneDrive/Documentos/Faculdade/2026_2/Monitoria/pitiupi/pitiupi.screenCast",
"project.structure.last.edited": "Modules",
"project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.2",
"settings.editor.selected.configurable": "preferences.keymap"
}
}]]></component>
<component name="RunManager">
<configuration default="true" type="JetRunConfigurationType">
<module name="pitiupi.screenCast" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="KotlinStandaloneScriptRunConfigurationType">
<module name="pitiupi.screenCast" />
<option name="filePath" />
<method v="2" />
</configuration>
<configuration default="true" type="PythonConfigurationType" factoryName="Python">
<module name="pitiupi.screenCast" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration default="true" type="Tox" factoryName="Tox">
<module name="pitiupi.screenCast" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<method v="2" />
</configuration>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-9823dce3aa75-bf35d07a577b-intellij.indexing.shared.core-IU-252.28539.97" />
</set>
</attachedChunks>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="083e72ce-fa9b-4ddf-a0ef-24105aa3eabb" name="Changes" comment="" />
<created>1784745206049</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1784745206049</updated>
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
</project>
@@ -1,6 +1,6 @@
#Thu, 16 Jul 2026 15:21:39 -0300 #Wed, 07 Oct 2026 15:36:37 -0300
/home/flavio/NetBeansProjects/pitiupi.screenCast= C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi.screenCast=
/home/flavio/NetBeansProjects/pitiupi= C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi=
Binary file not shown.
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="openjdk-26" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/pitiupi.screenCast.iml" filepath="$PROJECT_DIR$/pitiupi.screenCast.iml" />
</modules>
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../../pitiupi/dist/pitiupi.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>
@@ -4,28 +4,29 @@
*/ */
package pitiupi.screenCast.GUI; package pitiupi.screenCast.GUI;
import pitiupi.models.Menus.MulticastMenus.MulticastActionMenu;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import javax.swing.JMenu;
/** /**
* *
* @author flavio * @author flavio
*/ */
public class ScreenCastMenu extends JMenu { public class ScreenCastMenu extends MulticastActionMenu {
private final ScreenCastWindow screenCastWindow; private final ScreenCastWindow screenCastWindow;
private final javax.swing.JMenuItem menuItem;
public ScreenCastMenu(ScreenCastWindow screenCastWindow) { public ScreenCastMenu(ScreenCastWindow screenCastWindow) {
this.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);
} }
private void chatMenuActionPerformed(ActionEvent ae) { @Override
public void onClick() {
this.screenCastWindow.setVisible(true); this.screenCastWindow.setVisible(true);
} }
@Override
public String getText() {
return "Screen Cast";
}
} }
@@ -5,6 +5,9 @@
*/ */
package pitiupi.screenCast.GUI; package pitiupi.screenCast.GUI;
import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.plugin.Pitiupi;
import pitiupi.screenCast.control.AlarmClock; import pitiupi.screenCast.control.AlarmClock;
import pitiupi.screenCast.control.ImageTransformation; import pitiupi.screenCast.control.ImageTransformation;
import pitiupi.screenCast.control.ScreenShot; import pitiupi.screenCast.control.ScreenShot;
@@ -20,8 +23,6 @@ import java.util.logging.Logger;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JLabel; import javax.swing.JLabel;
import pitiupi.GUI.MainWindow;
import pitiupi.net.Message;
/** /**
* *
@@ -38,10 +39,11 @@ public class ScreenCastWindow extends javax.swing.JFrame {
public final static int TIME = 1000; public final static int TIME = 1000;
private float compressRatio; private float compressRatio;
private final MainWindow mainWindow;
public ScreenCastWindow(MainWindow mainWindow) { private final Pitiupi pitiupi;
this.mainWindow = mainWindow;
public ScreenCastWindow(Pitiupi pitiupi) {
this.pitiupi = pitiupi;
this.compressRatio = 0.5f; this.compressRatio = 0.5f;
this.initComponents(); this.initComponents();
} }
@@ -91,19 +93,12 @@ public class ScreenCastWindow extends javax.swing.JFrame {
BufferedImage image = ScreenShot.takeAShot(); BufferedImage image = ScreenShot.takeAShot();
BufferedImage rescaled = ImageTransformation.resize(image, 1024, 768); BufferedImage rescaled = ImageTransformation.resize(image, 1024, 768);
byte[] byteImage = ImageTransformation.compress(rescaled, compressRatio); byte[] byteImage = ImageTransformation.compress(rescaled, compressRatio);
ScreenCastMessage message = new ScreenCastMessage(this.mainWindow.getUserName(), byteImage); ScreenCastMessage message = new ScreenCastMessage(pitiupi.getUserName(), byteImage);
msg = message.toByteArray();
if (!pitiupi.sendUdpMulticast(message)) this.compressRatio -= 0.1f;
} catch (AWTException | IOException ex) { } catch (AWTException | IOException ex) {
Logger.getLogger(AlarmClock.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(AlarmClock.class.getName()).log(Level.SEVERE, null, ex);
} }
if (msg != null) {
try {
this.mainWindow.getSocket().send(msg);
} catch (IOException ex) {
System.out.println(ex.toString());
this.compressRatio -= 0.1f;
}
}
} }
public void receiveScreenCastMessage(ScreenCastMessage message) { public void receiveScreenCastMessage(ScreenCastMessage message) {
@@ -5,7 +5,8 @@
*/ */
package pitiupi.screenCast.net; package pitiupi.screenCast.net;
import pitiupi.net.Message;
import pitiupi.models.Messages.Message;
/** /**
* *
@@ -4,12 +4,12 @@
*/ */
package pitiupi.screenCast.plugin; package pitiupi.screenCast.plugin;
import java.io.ByteArrayInputStream; import java.util.List;
import java.io.IOException;
import java.io.ObjectInputStream; import pitiupi.models.Menus.MenuCategory;
import pitiupi.GUI.MainWindow; import pitiupi.models.Messages.Message;
import pitiupi.net.Message; import pitiupi.models.Plugin;
import pitiupi.plugin.Plugin; import pitiupi.plugin.Pitiupi;
import pitiupi.screenCast.GUI.ScreenCastMenu; import pitiupi.screenCast.GUI.ScreenCastMenu;
import pitiupi.screenCast.GUI.ScreenCastWindow; import pitiupi.screenCast.GUI.ScreenCastWindow;
import pitiupi.screenCast.net.ScreenCastMessage; import pitiupi.screenCast.net.ScreenCastMessage;
@@ -18,10 +18,9 @@ import pitiupi.screenCast.net.ScreenCastMessage;
* *
* @author flavio * @author flavio
*/ */
public class ScreenCastPlugin implements Plugin { public class ScreenCastPlugin extends Plugin {
private ScreenCastWindow screenCastWindow; private ScreenCastWindow screenCastWindow;
private MainWindow mainWindow;
@Override @Override
public String getName() { public String getName() {
@@ -39,31 +38,25 @@ public class ScreenCastPlugin implements Plugin {
} }
@Override @Override
public void createPlugin(MainWindow mainWindow) { public String getIdentifier() {
this.mainWindow = mainWindow; return "Pitiupi.ScreenCast.Example";
this.screenCastWindow = new ScreenCastWindow(mainWindow);
this.mainWindow.addMenu(new ScreenCastMenu(screenCastWindow));
} }
@Override @Override
public void receiveMessage(byte[] message) { public void createPlugin(Pitiupi pitiupi) {
ByteArrayInputStream bis = new ByteArrayInputStream(message); this.screenCastWindow = new ScreenCastWindow(pitiupi);
ObjectInputStream ois; pitiupi.createMenu(MenuCategory.Streaming, new ScreenCastMenu(screenCastWindow));
try { }
ois = new ObjectInputStream(bis);
if (ois.readObject() instanceof ScreenCastMessage msg) { @Override
this.screenCastWindow.receiveScreenCastMessage(msg); public List<Class<? extends Message>> getMessages() {
} return List.of(ScreenCastMessage.class);
} catch (IOException ex) { }
System.out.println(ex.toString());
} catch (ClassNotFoundException ex) { @Override
System.out.println(ex.toString()); public void receiveMessage(Message message) {
if (message instanceof ScreenCastMessage msg) {
this.screenCastWindow.receiveScreenCastMessage(msg);
} }
} }
@Override
public Message getMessage() {
return new ScreenCastMessage();
}
} }
+10
View File
@@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/pitiupi.testSocket.iml" filepath="$PROJECT_DIR$/pitiupi.testSocket.iml" />
</modules>
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>
+2
View File
@@ -8,3 +8,5 @@
# JARs gerados # JARs gerados
*.jar *.jar
.idea/
+5
View File
@@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
+6
View File
@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="LanguageDetectionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
</profile>
</component>
+9
View File
@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="pitiupi">
<CLASSES>
<root url="jar://$PROJECT_DIR$/dist/pitiupi.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+10
View File
@@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="plugins">
<CLASSES>
<root url="jar://$PROJECT_DIR$/plugins/pitiupi.chat.jar!/" />
<root url="jar://$PROJECT_DIR$/plugins/pitiupi.screenCast.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+5
View File
@@ -0,0 +1,5 @@
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_26_PREVIEW" project-jdk-name="openjdk-26" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/pitiupi.iml" filepath="$PROJECT_DIR$/pitiupi.iml" />
</modules>
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>
+547
View File
@@ -0,0 +1,547 @@
# 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.
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.
+512
View File
@@ -0,0 +1,512 @@
# Como criar plugins para o Pitiupi
Aqui serão discutidos os métodos e classes que um desenvolvedor pode ou deve usar para criar um plugin para a plataforma Pitiupi.
Alguns deles estarão marcados com um <span style="color: red;">[REF]</span>, isso indica que eles estão sendo alterados para a versão 3.0 e deixarão de existir ou vão mudar consideravelmente.
## GUI
O Pitiupi é feito usando a biblioteca Swing do Java, por isso é altamente recomendado que os plugins também a utilizem na hora de criar sua interface gráfica, porém não é obrigatório.
## Como tornar o seu plugin visível para o Pitiupi
A busca por plugins é realizada em três etapas pelo pitiupi:
1. Encontra todos os jars em `plugins`.
2. Verifica o `META-INF` dos jars para saber onde estão as implementações da classe `Plugin`.
3. Carrega o plugin no sistema.
Em relação ao `META-INF`, ele deve conter um diretório `resources` e dentro dele um arquivo contendo o nome completamente qualificado da classe `Plugin` do pitiupi: `pitiupi.models.Plugin`, por fim dentro desse arquivo você deve colocar o nome completamente qualificado de todas as classes que implementam `Plugin` do seu projeto, um em cada linha.
```
build/
|---->META-INF/resources/pitiupi.models.Plugin
|---->MeusPlugins/...
|---->MinhasMensagens/...
|---->Restante dos .class
```
```
src/MeusPlugins/MeuPrimeiroPlugin
src/MeusPlugins/MeuSegundoPlugin
...
```
### Dicas
- Faça um makefile que automaticamente mova o META-INF para o build/ quando você compilar uma nova versão caso ele esteja sendo alterado com frequência.
Ex:
```
JAR = ../pitiupi/dist/pitiupi.jar
BUILD_DIR = build
META-INF_DIR = src/META-INF
DIST = dist/mpChat.jar
SOURCES = $(shell find -name "*.java")
all:
mkdir -p "$(BUILD_DIR)"
mkdir -p "$(dir $(DIST))"
javac -cp "$(JAR)" -d "$(BUILD_DIR)" $(SOURCES)
cp -r "$(META-INF_DIR)" "$(BUILD_DIR)"
jar cf "$(DIST)" -C "$(BUILD_DIR)" .
clean:
rm -rf "$(BUILD_DIR)"/*
```
- Para a versão 3.0 será necessário fazer o mesmo processo para as implementações da classe `Message`, então se quiser deixar preparado para a nova versão adicione `pitiupi.models.Messages.Message` em `resources` e coloque nele todas suas implementações de `Message`, assim como você fez para os de `Plugin`.
## Pitiupi
A classe `Pitiupi` é a interface que o seu plugin vai receber quando for criado, ela fornece os métodos necessários para interagir com a interface gráfica e comunicação para outros peers.
### boolean sendUdpMulticast(Message message)
Envia uma mensagem multicast, retorna `boolean` indicando se teve sucesso.
### boolean sendUdpUnicast(Message message, InetAddress receiverAddress)
Envia uma mensagem unicast via udp para o endereço fornecido, retorna `boolean` indicando se teve sucesso.
### boolean sendTcp(Message message, InetAddress receiverAddress)
Envia uma mensagem unicast via tcp para o endereço fornecido, retorna `boolean` indicando se teve sucesso.
### <span style="color: red;">[REF]</span> TcpConnection connectTcp(InetAddress receiverAddress, String senderId, String receiverId)
Pede uma conexão tcp privada para o seu plugin com outro usuário, para utilizar essa funcionalidade é necessário implementar alguns métodos opcionais em Plugin. Maisinformações serão discutidas na seção TcpConnection.
### String getUserName()
Retorna o nome do usuário conectado
### int getPort()
Retorna a porta que o usuário está usando o Pitiupi
### void createMenu(MenuCategory category, Menu menu)
Adiciona um menu ao pitiupi, category define em qual das categoria de Menus ele será adicionado: Jogos, Social, Streaming.
Os `Menus` serão discutidos em uma seção posterior.
## Classe Plugin
Essa é a classe que o pitiupi vai procurar ao carregar o seu plugin, ela define um contrato de métodos que todo plugin deve possuir.
### getName()
Indica o nome público do plugin.
### getAuthor()
Indica o nome dos desenvolvedores do plugin.
### getVersion()
Indica a versão atual do plugin.
### getIdentifier()
Indica um id que o pitiupi vai utilizar para enontrar seu plugin, por isso é importante que ele seja bem especifico para evitar que dois plugins sejam confundidos durante a comunicação. Procure usar algo como: MeuPlugin.Autor.Números
### createPlugin(Pitiupi pitiupi)
Após o pitiupi identificar e carregar o seu plugin, ele vai chamar esse método para inicializá-lo, por isso pense nele como o construtor do seu plugin, é aqui que você inicializa atributos, adiciona os menus inciais, etc.
### getMessages()
Esse método indica ao pitiupi quais tipos de mensagem seu plugin quer receber, normalmente as que ele mesmo implementar.
Nesse caso, é normal que ele faça:
```
return List.of(
SuaMensagem1.class,
SuaMensagem2.class,
...
);
```
Caso você queira que seu plugin receba todo tipo de mensagem, existe um método `receiveAllTypes()` que ele pode sobreescrever para receber todas mensagens
que não sejam consideradas privadas.
### receiveMessage(Message message)
Esse é o método que o pitiupi vai chamar quando quiser te entregar uma mensagem pelo canal publico de mensagens. Note que ele entrega uma `Message`, então é necessário que
seu plugin confira o tipo real ele mesmo. Como em:
```
if (message instanceOf MinhaMensagem minhaMensagem) {...}
```
O pitiupi garante que é uma mensagem de um tipo que o seu plugin diz aceitar.
### <span style="color: red;">[REF]</span> métodos opcionais
Os métodos a seguir possuem uma implementação padrão da própria classe que retornam false. Os três ultimos são necessários caso seu plugin deseje criar e receber conexões privadas.
### <span style="color: red;">[REF]</span> receiveAllTypes()
Indica ao Pitiupi que seu plugin deseja receber todas as mensagens publicas da plataforma, independente do tipo.
### <span style="color: red;">[REF]</span> receivePrivateConnection(TcpConnection connection)
Esse método é chamado quando o Pitiupi precisa entregar uma `TcpConnection` ao seu plugin, seja um que você pediu ou recebeu de um peer.
### <span style="color: red;">[REF]</span> receiveBytes(TcpConnection connection, byte[] bytes)
Esse método é chamado para entregar as mensagens recebidas por um `TcpConnection`, o tcpPitiupi no parametro indica qual a fonte da mensagem.
### <span style="color: red;">[REF]</span> onSocketClosed(TcpConnection connection)
Esse é método é chamado quando o socket de um `TcpConnection` pertencente ao plugin é fechado.
## Message
Essa é a classe abstrata que o pitiupi usa para comunicação nos canais publicos.
<span style="color: red;">[REF]</span> Ela possui um atributo isPublic que por padrão é true, mas é possível defini-lo no construtor como false caso a mensagem deva ser privada.
Essa classe implementa `Serializable` e possui um método `toByteArray` que transforma a mensagem em `byte[]`. Porém é importante notar que caso ela possua um objeto de outra classe como atributo aquela classe também deve implementar `Serializable`.
## <span style="color: red;">[REF]</span> TcpConnection
Representa uma conexão Tcp ativa para algum outro peer, mensagens enviadas por essa conexão não chegam ao canal publico e em vez disso vão direto para destinatário.
Caso fique inativa (Sem enviar ou receber mensagens) por 5 minutos a conexão é fechada automaticamente e o dono é notificado.
### <span style="color: red;">[REF]</span> void send(byte[] message)
Envia um conjunto de bytes[] para o destinatario.
Caso queira enviar uma `Message` chame o método toByteArray para transforma-la em bytes primeiro.
### <span style="color: red;">[REF]</span> boolean isClosed()
Indica se a conexão esta fechada
### <span style="color: red;">[REF]</span> InetAddress getAddress()
Retorna o endereço do peer que ela esta conectada.
### <span style="color: red;">[REF]</span> String getReceiver()
Retorna o identificador do plugin que ela esta conectada.
### <span style="color: red;">[REF]</span> getReceiver()
Retorna o identifador do plugin que pediu pela conexão.
## Menu
Essa é a classe que indica ao pitiupi que um menu deve ser adicionado a janela principal da aplicação.
Menus podem ser de dois tipos `UnicastMenu` e `MulticastMenu` com a principal diferença entre eles sendo onde eles são adicionados na janela principal. Outras mudanças serão discutidas quando for relevante nas classes a seguir.
Atualmente Menus não podem ser alterados uma vez que são adicionados, então é importante que eles sejam feitos com isso em mente.
Tem um método `String getText()` que retorna o que deve ser escrito no Menu`.
### SubMenu
Permite que o `Menu` expanda em uma nova seção de menus.
`UnicastSubMenu` o método `getMenu()` retorna `List<UnicastMenu>`
`MulticastSubMenu` o método `getMenu()` retorna `List<MulticastMenu>`
Exemplo:
```
ChatCasualMulticastMenu chatCasualMulticastMenu;
ChatEstudoMutlticastMenu chatEstudoMutlticastMenu;
...
@Override
public List<MulticastMenu> getMenus() {
return List.of(chatCasualMulticastMenu, chatEstudoMutlticastMenu);
}
```
### ActionMenu
É um `Menu` que que realiza alguma ação quando selecionado pelo usuário.
`UnicastActionMenu` o método `onClick(User user)` é chamado quando selecionado pelo usuário, recebendo como parametro o peer escolhido pelo usuário.
`MulticastActionMenu` o método `onClick()` é chamado quando selecionado pelo usuário.
Exemplo:
```
@Override
public void onClick(User user) {
callManager.openCallWindow(user);
}
```
## Exemplo de plugin no pitiupi:
### Plugin
Implementa o contrato de plugin.
Inicializa a janela principal e cria os menus.
Também define quais tipos de mensagem ele quer receber.
```
public class ChatPlugin extends Plugin {
private ChatWindow chatWindow;
@Override
public void createPlugin(Pitiupi pitiupi) {
this.chatWindow = new ChatWindow(pitiupi);
pitiupi.createMenu(MenuCategory.Social, new ChatMenu(chatWindow));
}
@Override
public List<Class<? extends Message>> getMessages() {
return List.of(ChatMessage.class);
}
@Override
public void receiveMessage(Message message) {
if (message instanceof ChatMessage msg) {
this.chatWindow.receiveChatMessage(msg);
}
}
@Override
public String getName() {
return "Chat";
}
@Override
public String getAuthor() {
return "Flávio Schiavoni";
}
@Override
public String getVersion() {
return "0";
}
@Override
public String getIdentifier() {
return "Pitiupi.Chat.Example.123456";
}
}
```
### Message
Implementa uma mensagem customizada que o plugin vai utilizar para comunicação.
```
public class ChatMessage extends Message {
private String text;
private String name;
public ChatMessage(){
}
public ChatMessage(String name, String text){
this.name = name;
this.text = text;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the text
*/
public String getText() {
return text;
}
/**
* @param text the text to set
*/
public void setText(String text) {
this.text = text;
}
}
```
### Menus
Implementa os dois menus de Multicast utilizados pela aplicação.
#### ActionMenu
Esse menu apenas abre a janela principal do plugin.
```
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);
}
}
```
Esse menu abre a seção de menus do plugin Chat.
#### SubMenu
```
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);
}
}
```
### GUI
Essa é a janela principal do plugin de chat.
```
public class ChatWindow extends JDialog {
private JButton jButton1;
private JEditorPane jEditorPane1;
private JPanel jPanel1;
private JPanel jPanel2;
private JScrollPane jScrollPane1;
private JScrollPane jScrollPane2;
private JSplitPane jSplitPane1;
private JTextArea jTextArea1;
private final Pitiupi pitiupi;
public ChatWindow(Pitiupi pitiupi) {
this.pitiupi = pitiupi;
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
private void initComponents() {
this.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
this.setTitle("Chat Window");
this.setBounds(new java.awt.Rectangle(0, 0, 800, 600));
this.getContentPane().setLayout(new java.awt.BorderLayout(5, 5));
this.setPreferredSize(new java.awt.Dimension(800, 600));
jSplitPane1 = new JSplitPane();
jScrollPane1 = new JScrollPane();
jEditorPane1 = new JEditorPane();
jPanel1 = new JPanel();
jScrollPane2 = new JScrollPane();
jTextArea1 = new JTextArea();
jPanel2 = new JPanel();
jButton1 = new JButton();
jSplitPane1.setDividerLocation(500);
jSplitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT);
jEditorPane1.setContentType("text/html");
jPanel1.setLayout(new java.awt.BorderLayout());
jTextArea1.setColumns(20);
jTextArea1.setRows(5);
jTextArea1.addKeyListener(new java.awt.event.KeyAdapter() {
@Override
public void keyTyped(java.awt.event.KeyEvent evt) {
if (evt.getKeyChar() == '\n') {
sendMessage();
}
}
});
jButton1.setText("Send");
jScrollPane1.setViewportView(jEditorPane1);
jScrollPane2.setViewportView(jTextArea1);
jSplitPane1.setTopComponent(jScrollPane1);
jPanel1.add(jScrollPane2, java.awt.BorderLayout.CENTER);
jPanel2.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT));
jButton1.addActionListener((java.awt.event.ActionEvent evt) -> {
sendMessage();
});
jPanel2.add(jButton1);
jPanel1.add(jPanel2, java.awt.BorderLayout.PAGE_END);
jSplitPane1.setRightComponent(jPanel1);
getContentPane().add(jSplitPane1, java.awt.BorderLayout.CENTER);
pack();
}
private void sendMessage() {
if (jTextArea1.getText().trim().isEmpty()) {
return;
}
ChatMessage message = new ChatMessage(pitiupi.getUserName(), jTextArea1.getText());
this.sendChatMessage(message);
jTextArea1.setText("");
}
public void sendChatMessage(Message message) {
pitiupi.sendUdpMulticast(message);
}
public void receiveChatMessage(ChatMessage message) {
SimpleDateFormat dt = new SimpleDateFormat("dd/MM/yyyyy hh:mm:ss");
Date date = new Date();
this.receiveChatMessage(dt.format(date) + " "
+ "<b>" + message.getName() + "</b>: "
+ message.getText() + "<br>");
}
public void receiveChatMessage(String message) {
jEditorPane1.setText(message + jEditorPane1.getText());
}
}
```
+4 -4
View File
@@ -700,7 +700,7 @@ is divided into following sections:
<sequential> <sequential>
<j2seproject3:junit excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}"> <j2seproject3:junit excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
<customize> <customize>
<jvmarg value="-agentlib:jdwp=transport=${debug-transport},address=${jpda.address}"/> <jvmarg value="-agentlib:jdwp=transport=${debug-transport},receiverAddress=${jpda.receiverAddress}"/>
<customizeDebuggee/> <customizeDebuggee/>
</customize> </customize>
</j2seproject3:junit> </j2seproject3:junit>
@@ -843,7 +843,7 @@ is divided into following sections:
<attribute default="${debug.classpath}" name="classpath"/> <attribute default="${debug.classpath}" name="classpath"/>
<attribute default="" name="stopclassname"/> <attribute default="" name="stopclassname"/>
<sequential> <sequential>
<nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}"> <nbjpdastart addressproperty="jpda.receiverAddress" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
<modulepath> <modulepath>
<path path="@{modulepath}"/> <path path="@{modulepath}"/>
</modulepath> </modulepath>
@@ -882,7 +882,7 @@ is divided into following sections:
<sequential> <sequential>
<j2seproject1:java classname="@{classname}" classpath="@{classpath}" modulename="@{modulename}" modulepath="@{modulepath}"> <j2seproject1:java classname="@{classname}" classpath="@{classpath}" modulename="@{modulename}" modulepath="@{modulepath}">
<customize> <customize>
<jvmarg value="-agentlib:jdwp=transport=${debug-transport},address=${jpda.address}"/> <jvmarg value="-agentlib:jdwp=transport=${debug-transport},receiverAddress=${jpda.receiverAddress}"/>
<customizeDebuggee/> <customizeDebuggee/>
</customize> </customize>
</j2seproject1:java> </j2seproject1:java>
@@ -1434,7 +1434,7 @@ is divided into following sections:
</nbprofiledirect> </nbprofiledirect>
<j2seproject3:junit excludes="${excludes}" includes="${includes}" testincludes="${profile.class}" testmethods=""> <j2seproject3:junit excludes="${excludes}" includes="${includes}" testincludes="${profile.class}" testmethods="">
<customize> <customize>
<jvmarg value="-agentlib:jdwp=transport=${debug-transport},address=${jpda.address}"/> <jvmarg value="-agentlib:jdwp=transport=${debug-transport},receiverAddress=${jpda.receiverAddress}"/>
<env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/> <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
<jvmarg value="${profiler.info.jvmargs.agent}"/> <jvmarg value="${profiler.info.jvmargs.agent}"/>
<jvmarg line="${profiler.info.jvmargs}"/> <jvmarg line="${profiler.info.jvmargs}"/>
+2 -2
View File
@@ -46,8 +46,8 @@ javac.modulepath=
javac.processormodulepath= javac.processormodulepath=
javac.processorpath=\ javac.processorpath=\
${javac.classpath} ${javac.classpath}
javac.source=25 javac.source=17
javac.target=25 javac.target=17
javac.test.classpath=\ javac.test.classpath=\
${javac.classpath}:\ ${javac.classpath}:\
${build.classes.dir} ${build.classes.dir}
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
-1
View File
@@ -1 +0,0 @@
../../pitiupi.chat/dist/pitiupi.chat.jar
-1
View File
@@ -1 +0,0 @@
../../pitiupi.screenCast/dist/pitiupi.screenCast.jar
+72
View File
@@ -0,0 +1,72 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package pitiupi.GUI;
import javax.swing.*;
/**
* Janela que exibe informações sobre a aplicação, incluindo versão,
* descrição e autores.
*
* @author gustavo
*/
public class About extends javax.swing.JFrame {
public About () {
this.setDefaultCloseOperation(javax.swing.WindowConstants.HIDE_ON_CLOSE);
initComponents();
}
private void initComponents() {
this.setTitle("About");
this.setBounds(new java.awt.Rectangle(0, 0, 800, 600));
this.getContentPane().setLayout(new java.awt.BorderLayout(5, 5));
this.setPreferredSize(new java.awt.Dimension(800, 600));
JScrollPane scroll = getJScrollPane();
this.add(scroll);
}
private static JScrollPane getJScrollPane() {
JEditorPane texto = new JEditorPane();
texto.setContentType("text/html");
texto.setEditable(false);
texto.setText("""
<html>
<body>
<h1>Share My Sheet</h1>
\s
<p>
<b>Versão:</b> 1.0
</p>
\s
<p>
Plataforma P2P baseada em plugins para o desenvolvimento de aplicações
distribuídas. Criada para apoiar as práticas da disciplina de Redes de
Computadores e servir como base para projetos acadêmicos e TCCs.
\s
</p>
\s
<p>
<b>Autores:</b><br>
Flávio<br>
Tony<br>
Gustavo Henrique
</p>
\s
<p>
Universidade Federal de São João del-Rei (UFSJ)
</p>
\s
</body>
</html>
\s""");
return new JScrollPane(texto);
}
}
Loaded 100 of 139 files, more files were not shown because too many files have changed in this diff. Show more