89 changed files with 1276 additions and 756 deletions

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
#Mon, 17 Aug 2026 14:26:26 -0300 #Fri, 02 Oct 2026 20:11:21 -0300
C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi.chat= C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi.chat=
+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);
@@ -94,17 +92,13 @@ public class ChatWindow extends javax.swing.JDialog {
if (jTextArea1.getText().trim().isEmpty()) { if (jTextArea1.getText().trim().isEmpty()) {
return; return;
} }
ChatMessage message = new ChatMessage(mainWindow.getUserName(), 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.getSocketUDP().sendMulticast(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";
} }
} }
+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.
@@ -0,0 +1 @@
plugin.MultiProtocolChat
Binary file not shown.
Binary file not shown.
Binary file not shown.
File renamed without changes.
+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";
}
}
@@ -1,7 +1,7 @@
package GUI; package GUI;
import net.TestChatMessage; import net.MpChatMessage;
import pitiupi.GUI.MainWindow; import pitiupi.control.ConnectionManager;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -10,13 +10,13 @@ import java.net.UnknownHostException;
public abstract class ChatWindow extends JFrame { public abstract class ChatWindow extends JFrame {
protected final MainWindow mainWindow; protected final ConnectionManager connectionManager;
private JTextArea receivedMessagesArea; private JTextArea receivedMessagesArea;
protected JTextField messageField; protected JTextField messageField;
public ChatWindow(MainWindow mainWindow) { public ChatWindow(ConnectionManager connectionManager) {
this.mainWindow = mainWindow; this.connectionManager = connectionManager;
initComponents(); initComponents();
} }
@@ -63,20 +63,20 @@ public abstract class ChatWindow extends JFrame {
protected abstract void initButtons(JPanel bottomPanel); protected abstract void initButtons(JPanel bottomPanel);
protected TestChatMessage createMessage(String tipoMensagem) { protected MpChatMessage createMessage(String tipoMensagem) {
try { try {
String nome = mainWindow.getUserName(); String nome = connectionManager.getUserName();
String ip = InetAddress.getLocalHost().getHostAddress(); String ip = InetAddress.getLocalHost().getHostAddress();
String message = messageField.getText(); String message = messageField.getText();
return new TestChatMessage(nome, ip, tipoMensagem, message); return new MpChatMessage(nome, ip, tipoMensagem, message);
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public void receiveMessage(TestChatMessage msg) { public void receiveMessage(MpChatMessage msg) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
String mensagem = String.format( String mensagem = String.format(
"[%s] %s (%s): %s%n", "[%s] %s (%s): %s%n",
@@ -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";
}
}
@@ -1,7 +1,7 @@
package GUI; package GUI;
import net.TestChatMessage; import net.MpChatMessage;
import pitiupi.GUI.MainWindow; import pitiupi.control.ConnectionManager;
import pitiupi.net.tcp.TcpConnection; import pitiupi.net.tcp.TcpConnection;
import javax.swing.*; import javax.swing.*;
@@ -13,10 +13,9 @@ public class PrivateChatWindow extends ChatWindow {
private final TcpConnection connection; private final TcpConnection connection;
public PrivateChatWindow(MainWindow mainWindow, TcpConnection connection) { public PrivateChatWindow(ConnectionManager ConnectionManager, TcpConnection connection) {
this.connection = connection; super(ConnectionManager);
this.connection = connection;
super(mainWindow);
} }
@Override @Override
@@ -34,13 +33,11 @@ public class PrivateChatWindow extends ChatWindow {
private void sendTCP() { private void sendTCP() {
try { try {
TestChatMessage msg = createMessage("TCP Private"); MpChatMessage msg = createMessage("TCP Private");
connection.send(msg.toByteArray()); connection.send(msg.toByteArray());
} catch (Exception ex) { } catch (Exception ex) {
Logger.getLogger( Logger.getLogger(PrivateChatWindow.class.getName()).log(Level.SEVERE, null, ex);
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 "";
}
}
@@ -1,7 +1,7 @@
package GUI; package GUI;
import net.TestChatMessage; import net.MpChatMessage;
import pitiupi.GUI.MainWindow; import pitiupi.control.ConnectionManager;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -13,8 +13,8 @@ public class PublicChatWindow extends ChatWindow {
private JTextField ipField; private JTextField ipField;
public PublicChatWindow(MainWindow mainWindow) { public PublicChatWindow(ConnectionManager connectionManager) {
super(mainWindow); super(connectionManager);
} }
@Override @Override
@@ -56,11 +56,11 @@ public class PublicChatWindow extends ChatWindow {
private void sendTCP() { private void sendTCP() {
try { try {
TestChatMessage msg = createMessage("TCP Unicast"); MpChatMessage msg = createMessage("TCP Unicast");
InetAddress ip = InetAddress.getByName(ipField.getText()); InetAddress ip = InetAddress.getByName(ipField.getText());
mainWindow.getSocketTCP().send(msg, ip); connectionManager.sendTcp(msg, ip);
} catch (Exception ex) { } catch (Exception ex) {
Logger.getLogger(PublicChatWindow.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(PublicChatWindow.class.getName()).log(Level.SEVERE, null, ex);
@@ -69,11 +69,11 @@ public class PublicChatWindow extends ChatWindow {
private void sendUDP() { private void sendUDP() {
try { try {
TestChatMessage msg = createMessage("UDP Unicast"); MpChatMessage msg = createMessage("UDP Unicast");
InetAddress ip = InetAddress.getByName(ipField.getText()); InetAddress ip = InetAddress.getByName(ipField.getText());
mainWindow.getSocketUDP().sendUnicast(msg, ip); connectionManager.sendUdpUnicast(msg, ip);
} catch (Exception ex) { } catch (Exception ex) {
Logger.getLogger(PublicChatWindow.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(PublicChatWindow.class.getName()).log(Level.SEVERE, null, ex);
@@ -82,9 +82,9 @@ public class PublicChatWindow extends ChatWindow {
private void sendMulticast() { private void sendMulticast() {
try { try {
TestChatMessage msg = createMessage("UDP Multicast"); MpChatMessage msg = createMessage("UDP Multicast");
mainWindow.getSocketUDP().sendMulticast(msg); connectionManager.sendUdpMulticast(msg);
} catch (Exception ex) { } catch (Exception ex) {
Logger.getLogger(PublicChatWindow.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(PublicChatWindow.class.getName()).log(Level.SEVERE, null, ex);
@@ -0,0 +1 @@
plugin.MultiProtocolChat
@@ -4,25 +4,28 @@ import GUI.ChatMenu;
import GUI.ChatWindow; import GUI.ChatWindow;
import GUI.PrivateChatWindow; import GUI.PrivateChatWindow;
import GUI.PublicChatWindow; import GUI.PublicChatWindow;
import net.TestChatMessage; import net.MpChatMessage;
import pitiupi.GUI.MainWindow; import pitiupi.control.ConnectionManager;
import pitiupi.control.PeerListener;
import pitiupi.models.User;
import pitiupi.net.tcp.TcpConnection; import pitiupi.net.tcp.TcpConnection;
import javax.swing.*; import javax.swing.*;
import java.net.InetAddress;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
public class ChatController { public class ChatController implements PeerListener {
MainWindow mainWindow; ConnectionManager connectionManager;
ChatMenu chatMenu; ChatMenu chatMenu;
PublicChatWindow publicChatWindow; PublicChatWindow publicChatWindow;
private final HashMap<TcpConnection, JMenuItem> chatMenus;
private final HashMap<TcpConnection, ChatWindow> chatWindows; private final HashMap<TcpConnection, ChatWindow> chatWindows;
public ChatController(MainWindow mainWindow, ChatMenu chatMenu) { public ChatController(ConnectionManager connectionManager, ChatMenu chatMenu) {
this.mainWindow = mainWindow; this.connectionManager = connectionManager;
publicChatWindow = new PublicChatWindow(mainWindow); publicChatWindow = new PublicChatWindow(connectionManager);
chatMenus = new HashMap<>(); chatMenus = new HashMap<>();
chatWindows = new HashMap<>(); chatWindows = new HashMap<>();
@@ -35,7 +38,7 @@ public class ChatController {
public void addChatWindow(TcpConnection tcpConnection) { public void addChatWindow(TcpConnection tcpConnection) {
if (chatWindows.containsKey(tcpConnection)) return; if (chatWindows.containsKey(tcpConnection)) return;
PrivateChatWindow newChat = new PrivateChatWindow(mainWindow, tcpConnection); PrivateChatWindow newChat = new PrivateChatWindow(connectionManager, tcpConnection);
chatWindows.put(tcpConnection, newChat); chatWindows.put(tcpConnection, newChat);
JMenuItem newMenuItem = new JMenuItem(); JMenuItem newMenuItem = new JMenuItem();
chatMenus.put(tcpConnection, newMenuItem); chatMenus.put(tcpConnection, newMenuItem);
@@ -52,10 +55,19 @@ public class ChatController {
removedWindow.dispose(); removedWindow.dispose();
} }
public void receivePublic(TestChatMessage message) { public void receivePublic(MpChatMessage message) {
publicChatWindow.receiveMessage(message); publicChatWindow.receiveMessage(message);
} }
public void receivePrivate(TcpConnection tcpConnection, TestChatMessage message) { public void receivePrivate(TcpConnection tcpConnection, MpChatMessage message) {
chatWindows.get(tcpConnection).receiveMessage(message); chatWindows.get(tcpConnection).receiveMessage(message);
} }
public void openChatWindow(InetAddress address) {
}
@Override
public void onPeersChanged(List<User> list) {
}
} }
@@ -1,21 +1,21 @@
package net; package net;
import pitiupi.net.Message; import pitiupi.models.Messages.Message;
public class TestChatMessage extends Message { public class MpChatMessage extends Message {
private String nome; private String nome;
private String ip; private String ip;
private String tipoMensagem; private String tipoMensagem;
private String message; private String message;
public TestChatMessage(String nome, String ip, String tipoMensagem, String message) { public MpChatMessage(String nome, String ip, String tipoMensagem, String message) {
this.nome = nome; this.nome = nome;
this.ip = ip; this.ip = ip;
this.tipoMensagem = tipoMensagem; this.tipoMensagem = tipoMensagem;
this.message = message; this.message = message;
} }
public TestChatMessage(){} public MpChatMessage(){}
public String getNome() { public String getNome() {
return nome; return nome;
@@ -2,11 +2,12 @@ package plugin;
import GUI.ChatMenu; import GUI.ChatMenu;
import control.ChatController; import control.ChatController;
import net.TestChatMessage; import net.MpChatMessage;
import pitiupi.GUI.MainWindow; import pitiupi.control.ConnectionManager;
import pitiupi.net.Message; import pitiupi.control.GuiManager;
import pitiupi.models.Messages.Message;
import pitiupi.net.tcp.TcpConnection; import pitiupi.net.tcp.TcpConnection;
import pitiupi.plugin.PrivateConnectionPlugin; import pitiupi.models.Plugin;
import javax.swing.*; import javax.swing.*;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -14,9 +15,10 @@ import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.net.InetAddress; import java.net.InetAddress;
public class TestChat implements PrivateConnectionPlugin{ public class MultiProtocolChat extends Plugin {
ChatController chatController; ChatController chatController;
MainWindow mainWindow; ConnectionManager connectionManager;
GuiManager guiManager;
@Override @Override
public String getName() { public String getName() {
@@ -39,47 +41,50 @@ public class TestChat implements PrivateConnectionPlugin{
} }
@Override @Override
public void createPlugin(MainWindow mainWindow) { public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) {
this.mainWindow = mainWindow; this.connectionManager = connectionManager;
this.guiManager = guiManager;
ChatMenu chatMenu = new ChatMenu(this); ChatMenu chatMenu = new ChatMenu(this);
chatController = new ChatController(mainWindow, chatMenu); chatController = new ChatController(connectionManager, chatMenu);
mainWindow.addMenu(chatMenu); guiManager.addMulticastMenu(chatMenu);
} }
@Override @Override
public Message getMessage() { public Message getMessage() {
return new TestChatMessage(); return new MpChatMessage();
} }
@Override @Override
public void receiveMessage(byte[] message) { public void receiveMessage(byte[] message) {
TestChatMessage testChatMessage = parseMessage(message); MpChatMessage mpChatMessage = parseMessage(message);
chatController.receivePublic(testChatMessage); chatController.receivePublic(mpChatMessage);
} }
@Override @Override
public void receivePrivateConnection(TcpConnection tcpConnection) { public boolean receivePrivateConnection(TcpConnection connection) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
chatController.addChatWindow(tcpConnection); chatController.addChatWindow(connection);
}); });
return true;
} }
@Override @Override
public void receiveBytes(TcpConnection connection, byte[] bytes) { public boolean receiveBytes(TcpConnection connection, byte[] bytes) {
TestChatMessage testChatMessage = parseMessage(bytes); MpChatMessage mpChatMessage = parseMessage(bytes);
chatController.receivePrivate(connection, testChatMessage); chatController.receivePrivate(connection, mpChatMessage);
return true;
} }
@Override @Override
public void onSocketClosed(TcpConnection tcpConnection) { public boolean onSocketClosed(TcpConnection connection) {
chatController.removeChatWindow(tcpConnection); chatController.removeChatWindow(connection);
return true;
} }
public boolean openConnection(String ip) { public boolean openConnection(String ip) {
try { try {
mainWindow.getSocketTCP().createPrivateConnection(InetAddress.getByName(ip), getIdentifier(), getIdentifier()); connectionManager.connectPluginTcp(InetAddress.getByName(ip), getIdentifier(), getIdentifier());
return true; return true;
} }
catch (Exception e) { catch (Exception e) {
@@ -88,12 +93,12 @@ public class TestChat implements PrivateConnectionPlugin{
return false; return false;
} }
private TestChatMessage parseMessage(byte[] bytes) { private MpChatMessage parseMessage(byte[] bytes) {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois; ObjectInputStream ois;
try { try {
ois = new ObjectInputStream(bis); ois = new ObjectInputStream(bis);
if (ois.readObject() instanceof TestChatMessage msg) { if (ois.readObject() instanceof MpChatMessage msg) {
return msg; return msg;
} }
} catch (IOException | ClassNotFoundException ex) { } catch (IOException | ClassNotFoundException ex) {
+23 -31
View File
@@ -5,24 +5,10 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="083e72ce-fa9b-4ddf-a0ef-24105aa3eabb" name="Changes" comment=""> <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 afterPath="$PROJECT_DIR$/../pitiupi.testSocket/Makefile" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../.idea/vcs.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/../.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.chat/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/build/built-jar.properties" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.chat/build/built-jar.properties" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow.class" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.chat/build/classes/pitiupi/chat/GUI/ChatWindow.class" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/dist/pitiupi.chat.jar" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/pitiupi.chat.iml" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.chat/pitiupi.chat.iml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi.chat/src/pitiupi/chat/GUI/ChatWindow.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi.chat/src/pitiupi/chat/GUI/ChatWindow.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/build/built-jar.properties" beforeDir="false" afterPath="$PROJECT_DIR$/build/built-jar.properties" afterDir="false" /> <change beforePath="$PROJECT_DIR$/../pitiupi/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/build/classes/pitiupi/screenCast/GUI/ScreenCastWindow.class" beforeDir="false" afterPath="$PROJECT_DIR$/build/classes/pitiupi/screenCast/GUI/ScreenCastWindow.class" afterDir="false" /> <change beforePath="$PROJECT_DIR$/../pitiupi/src/pitiupi/net/tcp/ConnectionFactory.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/src/pitiupi/net/tcp/ConnectionFactory.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/dist/pitiupi.screenCast.jar" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/src/pitiupi/screenCast/GUI/ScreenCastWindow.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/pitiupi/screenCast/GUI/ScreenCastWindow.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi/plugins/pitiupi.chat.jar" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/plugins/pitiupi.chat.jar" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi/plugins/pitiupi.screenCast.jar" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/plugins/pitiupi.screenCast.jar" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi/src/pitiupi/GUI/MainWindow.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/src/pitiupi/GUI/MainWindow.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../pitiupi/src/pitiupi/control/HeartbeatManager.java" beforeDir="false" afterPath="$PROJECT_DIR$/../pitiupi/src/pitiupi/control/HeartbeatManager.java" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -46,10 +32,12 @@
"RunOnceActivity.ShowReadmeOnStart": "true", "RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true", "RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "feature/unicast", "RunOnceActivity.typescript.service.memoryLimit.init": "true",
"codeWithMe.voiceChat.enabledByDefault": "false",
"git-widget-placeholder": "feature/separacao-gerenciadores",
"ignore.virus.scanning.warn.message": "true", "ignore.virus.scanning.warn.message": "true",
"kotlin-language-version-configured": "true", "kotlin-language-version-configured": "true",
"last_opened_file_path": "C:/Users/extre/OneDrive/Documentos/Faculdade/2026_02/Monitoria/Pitiupi/pitiupi.screenCast", "last_opened_file_path": "C:/Users/Gustavo/OneDrive/Documentos/Faculdade/2026_2/Monitoria/pitiupi/pitiupi.screenCast",
"project.structure.last.edited": "Modules", "project.structure.last.edited": "Modules",
"project.structure.proportion": "0.15", "project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.2", "project.structure.side.proportion": "0.2",
@@ -57,17 +45,6 @@
} }
}]]></component> }]]></component>
<component name="RunManager"> <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"> <configuration default="true" type="PythonConfigurationType" factoryName="Python">
<module name="pitiupi.screenCast" /> <module name="pitiupi.screenCast" />
<option name="ENV_FILES" value="" /> <option name="ENV_FILES" value="" />
@@ -102,11 +79,23 @@
<option name="ADD_SOURCE_ROOTS" value="true" /> <option name="ADD_SOURCE_ROOTS" value="true" />
<method v="2" /> <method v="2" />
</configuration> </configuration>
<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>
</component> </component>
<component name="SharedIndexes"> <component name="SharedIndexes">
<attachedChunks> <attachedChunks>
<set> <set>
<option value="bundled-jdk-9823dce3aa75-bf35d07a577b-intellij.indexing.shared.core-IU-252.28539.54" /> <option value="bundled-jdk-30f59d01ecdd-37e91769500f-intellij.indexing.shared.core-IU-261.26222.65" />
<option value="bundled-js-predefined-d6986cc7102b-31caf2ab9e3c-JavaScript-IU-261.26222.65" />
</set> </set>
</attachedChunks> </attachedChunks>
</component> </component>
@@ -120,4 +109,7 @@
</task> </task>
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
</project> </project>
@@ -1,6 +1,6 @@
#Mon, 17 Aug 2026 14:35:40 -0300 #Tue, 29 Sep 2026 11:04:58 -0300
C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi.screenCast= C\:\\Users\\Gustavo\\OneDrive\\Documentos\\Faculdade\\2026_2\\Monitoria\\pitiupi\\pitiupi.screenCast=
C\:\\Users\\extre\\OneDrive\\Documentos\\Faculdade\\2026_02\\Monitoria\\Pitiupi\\pitiupi= C\:\\Users\\Gustavo\\OneDrive\\Documentos\\Faculdade\\2026_2\\Monitoria\\pitiupi\\pitiupi=
Binary file not shown.
@@ -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>
@@ -5,6 +5,8 @@
*/ */
package pitiupi.screenCast.GUI; package pitiupi.screenCast.GUI;
import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
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;
@@ -38,10 +40,13 @@ 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; private final ConnectionManager connectionManager;
private final GuiManager guiManager;
public ScreenCastWindow(ConnectionManager connectionManager, GuiManager guiManager) {
this.connectionManager = connectionManager;
this.guiManager = guiManager;
public ScreenCastWindow(MainWindow mainWindow) {
this.mainWindow = mainWindow;
this.compressRatio = 0.5f; this.compressRatio = 0.5f;
this.initComponents(); this.initComponents();
} }
@@ -91,19 +96,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(this.connectionManager.getUserName(), byteImage);
msg = message.toByteArray();
if (!connectionManager.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.getSocketUDP().sendMulticast(msg);
} catch (IOException ex) {
System.out.println(ex.toString());
this.compressRatio -= 0.1f;
}
}
} }
public void receiveScreenCastMessage(ScreenCastMessage message) { public void receiveScreenCastMessage(ScreenCastMessage message) {
@@ -8,6 +8,8 @@ import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import pitiupi.GUI.MainWindow; import pitiupi.GUI.MainWindow;
import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.net.Message; import pitiupi.net.Message;
import pitiupi.plugin.Plugin; import pitiupi.plugin.Plugin;
import pitiupi.screenCast.GUI.ScreenCastMenu; import pitiupi.screenCast.GUI.ScreenCastMenu;
@@ -21,7 +23,6 @@ import pitiupi.screenCast.net.ScreenCastMessage;
public class ScreenCastPlugin implements Plugin { public class ScreenCastPlugin implements Plugin {
private ScreenCastWindow screenCastWindow; private ScreenCastWindow screenCastWindow;
private MainWindow mainWindow;
@Override @Override
public String getName() { public String getName() {
@@ -39,10 +40,14 @@ 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
public void createPlugin(ConnectionManager connectionManager, GuiManager guiManager) {
this.screenCastWindow = new ScreenCastWindow(connectionManager, guiManager);
guiManager.addMenu(new ScreenCastMenu(screenCastWindow));
} }
@Override @Override
@@ -54,9 +59,7 @@ public class ScreenCastPlugin implements Plugin {
if (ois.readObject() instanceof ScreenCastMessage msg) { if (ois.readObject() instanceof ScreenCastMessage msg) {
this.screenCastWindow.receiveScreenCastMessage(msg); this.screenCastWindow.receiveScreenCastMessage(msg);
} }
} catch (IOException ex) { } catch (IOException | ClassNotFoundException ex) {
System.out.println(ex.toString());
} catch (ClassNotFoundException ex) {
System.out.println(ex.toString()); System.out.println(ex.toString());
} }
} }
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
plugin.TestChat
Binary file not shown.
-84
View File
@@ -1,84 +0,0 @@
package GUI;
import plugin.TestChat;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
public class ChatMenu extends JMenu {
private final TestChat testChat;
public ChatMenu(TestChat testChat) {
this.testChat = testChat;
this.setText("MP-Chat");
JMenuItem startPrivateChat = new JMenuItem("start Private Chat");
startPrivateChat.addActionListener(_ -> showPrivateChatDialog());
this.add(startPrivateChat);
}
public void newMenuItem(JMenuItem newMenuItem, ChatWindow chatWindow) {
newMenuItem.addActionListener(_ -> chatWindow.setVisible(true));
this.add(newMenuItem);
}
public void removeMenuItem(JMenuItem removeMenuItem) {
this.remove(removeMenuItem);
}
private void showPrivateChatDialog() {
JDialog dialog = new JDialog(
SwingUtilities.getWindowAncestor(this),
"Novo chat privado",
Dialog.ModalityType.APPLICATION_MODAL
);
JTextField ipField = new JTextField();
JButton startButton = new JButton("Iniciar");
JButton cancelButton = new JButton("Cancelar");
JPanel contentPanel = new JPanel(new BorderLayout(10, 10));
contentPanel.setBorder(
BorderFactory.createEmptyBorder(10, 10, 10, 10)
);
JPanel ipPanel = new JPanel(new BorderLayout(5, 5));
ipPanel.add(new JLabel("IP:"), BorderLayout.WEST);
ipPanel.add(ipField, BorderLayout.CENTER);
JPanel buttonsPanel = new JPanel(
new FlowLayout(FlowLayout.RIGHT)
);
buttonsPanel.add(cancelButton);
buttonsPanel.add(startButton);
JLabel errorLabel = new JLabel("Não foi possível estabelecer a conexão.");
errorLabel.setForeground(Color.RED);
errorLabel.setVisible(false);
contentPanel.add(errorLabel, BorderLayout.NORTH);
contentPanel.add(ipPanel, BorderLayout.CENTER);
contentPanel.add(buttonsPanel, BorderLayout.SOUTH);
cancelButton.addActionListener(e -> dialog.dispose());
startButton.addActionListener(e -> {
String ip = ipField.getText();
if (testChat.openConnection(ip)) dialog.dispose();
else errorLabel.setVisible(true);
});
dialog.setContentPane(contentPanel);
dialog.pack();
dialog.setLocationRelativeTo(SwingUtilities.getWindowAncestor(this));
dialog.setVisible(true);
}
}
@@ -1 +0,0 @@
plugin.TestChat
+1 -1
View File
@@ -1,5 +1,5 @@
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="openjdk-26" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_26_PREVIEW" project-jdk-name="openjdk-26" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>
+2 -2
View File
@@ -468,10 +468,10 @@ MeusPlugins
| |
+---services +---services
| |
+---pitiupi.plugin.Plugin +---pitiupi.models.Plugin
``` ```
O arquivo `pitiupi.plugin.Plugin` deve conter o nome completo das classes que O arquivo `pitiupi.models.Plugin` deve conter o nome completo das classes que
implementam a interface Plugin: implementam a interface Plugin:
``` ```
+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}
+51 -118
View File
@@ -5,14 +5,13 @@
*/ */
package pitiupi.GUI; package pitiupi.GUI;
import pitiupi.net.tcp.SocketTCP; import pitiupi.control.ConnectionManager;
import pitiupi.net.udp.SocketUDP; import pitiupi.control.GuiManager;
import java.util.ArrayList; import pitiupi.control.plugins.PluginManager;
import java.util.List;
import javax.swing.JMenu; import javax.swing.*;
import pitiupi.control.PluginLoader;
import pitiupi.plugin.Plugin; import java.awt.*;
import pitiupi.control.HeartbeatManager;
/** /**
* Janela principal da aplicação. * Janela principal da aplicação.
@@ -28,28 +27,15 @@ import pitiupi.control.HeartbeatManager;
* @author tony * @author tony
* @author Gustavo * @author Gustavo
*/ */
public class MainWindow extends javax.swing.JFrame { public class MainWindow extends JFrame {
private final MenuBar menuBar; private MenuBar menuBar;
private javax.swing.JScrollPane jScrollPane1; private JLabel status;
private javax.swing.JLabel status; OnlineUsersPanel onlineUsersPanel;
private javax.swing.JPanel statusPanel;
private javax.swing.JTabbedPane tabbedPane;
private HeartbeatManager heartbeatManager; private final ConnectionManager connectionManager;
private OnlineUsersPanel onlineUsersPanel; private final PluginManager pluginManager;
private final GuiManager guiManager;
private SocketUDP socketUDP;
private SocketTCP socketTCP;
private String userName;
private int port;
private String pitiupiIdentifier;
private PluginLoader pluginManager;
private final List<Plugin> plugins;
/** /**
* Cria a janela principal da aplicação. * Cria a janela principal da aplicação.
@@ -58,62 +44,53 @@ public class MainWindow extends javax.swing.JFrame {
* @param port porta utilizada na comunicação multicast. * @param port porta utilizada na comunicação multicast.
*/ */
public MainWindow(String userName, int port) { public MainWindow(String userName, int port) {
this.userName = userName; initMenu();
this.port = port;
this.pitiupiIdentifier = "Pitiupi.Alice.2026"; pluginManager = new PluginManager();
this.menuBar = new MenuBar(this); connectionManager = new ConnectionManager(this, userName, pluginManager, port);
this.plugins = new ArrayList<>();
this.initComponents(); initUserPanel();
} initStatusPanel();
guiManager = new GuiManager(this, menuBar, onlineUsersPanel);
loadPlugins();
this.pack();
private void initComponents() {
initGUI();
initSocket();
initHeartbeat();
initPlugins();
this.setSize(new java.awt.Dimension(1024, 768)); this.setSize(new java.awt.Dimension(1024, 768));
} }
private void initGUI() { private void initMenu() {
menuBar = new MenuBar(this);
this.setJMenuBar(menuBar); this.setJMenuBar(menuBar);
this.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); this.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
this.setTitle("Share My Sheet"); this.setTitle("Pitiupi");
jScrollPane1 = new javax.swing.JScrollPane(); javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
tabbedPane = new javax.swing.JTabbedPane(); javax.swing.JTabbedPane tabbedPane = new javax.swing.JTabbedPane();
statusPanel = new javax.swing.JPanel();
status = new javax.swing.JLabel();
jScrollPane1.setViewportView(tabbedPane); jScrollPane1.setViewportView(tabbedPane);
statusPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT));
status.setText("Connected on port " + this.port + " name " + this.userName);
statusPanel.add(status);
this.getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER); this.getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER);
this.getContentPane().add(statusPanel, java.awt.BorderLayout.PAGE_END);
this.pack();
} }
private void initSocket() { private void initUserPanel() {
this.socketUDP = new SocketUDP(this); onlineUsersPanel = new OnlineUsersPanel();
this.socketTCP = new SocketTCP(this); this.connectionManager.addPeerListener(onlineUsersPanel);
this.getContentPane().add(onlineUsersPanel, java.awt.BorderLayout.LINE_END);
this.socketUDP.start();
this.socketTCP.start();
} }
private void initHeartbeat() { private void initStatusPanel() {
this.heartbeatManager = new HeartbeatManager(this); JPanel statusPanel = new JPanel();
this.onlineUsersPanel = new OnlineUsersPanel(); status = new JLabel();
this.heartbeatManager.addPeerListener(this.onlineUsersPanel);
this.getContentPane().add(this.onlineUsersPanel, java.awt.BorderLayout.LINE_END);
this.heartbeatManager.start();
}
private void initPlugins() { statusPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
this.pluginManager = new PluginLoader(this); updateStatus();
this.pluginManager.loadPlugins(); statusPanel.add(status);
this.getContentPane().add(statusPanel, BorderLayout.PAGE_END);
} }
public void exit() { public void exit() {
@@ -122,65 +99,21 @@ public class MainWindow extends javax.swing.JFrame {
System.exit(0); System.exit(0);
} }
public HeartbeatManager getHeartbeatManager() { public ConnectionManager getConnectionManager() {
return heartbeatManager; return connectionManager;
} }
public String getUserName(){ public void loadPlugins() {
return this.userName; pluginManager.loadPlugins(connectionManager, guiManager);
}
public String getIdentifier(){
return this.pitiupiIdentifier;
}
public void addMenu(JMenu menu) {
this.menuBar.addPlugin(menu);
}
public int getPort() {
return this.port;
}
public SocketUDP getSocketUDP() {
return this.socketUDP;
}
public SocketTCP getSocketTCP() {
return this.socketTCP;
}
public List<Plugin> getPlugins() {
return plugins;
} }
public void setUserName(String userName) { public void setUserName(String userName) {
this.userName = userName; connectionManager.setUserName(userName);
updateStatus();
}
public void setPort(int port) {
this.port = port;
reconnect();
updateStatus(); updateStatus();
} }
private void updateStatus() { private void updateStatus() {
this.status.setText("Connected on port " + this.port + " name " + this.userName); this.status.setText("Connected on port " + connectionManager.getPort() + " name " + connectionManager.getUserName());
} }
private void reconnect() {
if (this.socketUDP != null) {
this.socketUDP.close();
}
if (this.socketTCP != null) {
this.socketTCP.close();
}
this.socketUDP = new SocketUDP(this);
this.socketTCP = new SocketTCP(this);
this.socketUDP.start();
this.socketTCP.start();
}
} }
+51 -36
View File
@@ -5,8 +5,14 @@
*/ */
package pitiupi.GUI; package pitiupi.GUI;
import javax.swing.JMenu; import pitiupi.models.Menus.MenuCategory;
import javax.swing.JMenuBar;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map;
/** /**
* Barra de menus principal da aplicação. * Barra de menus principal da aplicação.
@@ -21,12 +27,14 @@ import javax.swing.JMenuBar;
*/ */
public class MenuBar extends JMenuBar { public class MenuBar extends JMenuBar {
private javax.swing.JMenuItem setupMenu; private JMenuItem preferencesMenu;
private javax.swing.JMenuItem aboutMenu; private JMenuItem aboutMenu;
private javax.swing.JMenu fileMenu; private JMenu fileMenu;
private javax.swing.JMenu helpMenu; private JMenu helpMenu;
private javax.swing.JMenu pluginMenu; private JMenu pluginMenu;
private javax.swing.JMenuItem exitMenu; private Map<MenuCategory, JMenu> categories;
private JMenuItem exitMenu;
private final MainWindow mainWindow; private final MainWindow mainWindow;
/** /**
@@ -40,63 +48,70 @@ public class MenuBar extends JMenuBar {
} }
private void initComponents() { private void initComponents() {
fileMenu = new javax.swing.JMenu(); initFileMenu();
exitMenu = new javax.swing.JMenuItem(); initPluginMenu();
helpMenu = new javax.swing.JMenu(); initHelpMenu();
pluginMenu = new javax.swing.JMenu(); }
setupMenu = new javax.swing.JMenuItem();
aboutMenu = new javax.swing.JMenuItem();
fileMenu.setText("File"); private void initFileMenu() {
exitMenu.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F4, java.awt.event.InputEvent.ALT_MASK)); fileMenu = new JMenu("File");
exitMenu.setText("Exit"); exitMenu = new JMenuItem("Exit");
exitMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_DOWN_MASK));
exitMenu.addActionListener(this::exitMenuActionPerformed); exitMenu.addActionListener(this::exitMenuActionPerformed);
fileMenu.add(exitMenu); fileMenu.add(exitMenu);
this.add(fileMenu); this.add(fileMenu);
}
private void initPluginMenu() {
pluginMenu = new JMenu("Plugin");
categories = new HashMap<>();
for (MenuCategory category : MenuCategory.values()) {
JMenu newCategoryMenu = new JMenu(category.toString());
pluginMenu.add(newCategoryMenu);
categories.put(category, newCategoryMenu);
}
pluginMenu.setText("Plugin");
this.add(pluginMenu); this.add(pluginMenu);
}
helpMenu.setText("Help"); private void initHelpMenu() {
helpMenu = new JMenu("Help");
preferencesMenu = new JMenuItem("Preferences");
aboutMenu = new JMenuItem("About");
setupMenu.setText("Preferences"); preferencesMenu.addActionListener(this::setupMenuActionPerformed);
setupMenu.addActionListener(this::setupMenuActionPerformed); helpMenu.add(preferencesMenu);
helpMenu.add(setupMenu);
aboutMenu.setText("About");
aboutMenu.addActionListener(this::aboutMenuActionPerformed); aboutMenu.addActionListener(this::aboutMenuActionPerformed);
helpMenu.add(aboutMenu); helpMenu.add(aboutMenu);
this.add(helpMenu); this.add(helpMenu);
} }
private void exitMenuActionPerformed(java.awt.event.ActionEvent evt) { private void exitMenuActionPerformed(ActionEvent evt) {
mainWindow.exit(); mainWindow.exit();
} }
private void setupMenuActionPerformed(ActionEvent evt) {
private void setupMenuActionPerformed(java.awt.event.ActionEvent evt) {
Login login = new Login(); Login login = new Login();
login.setVisible(true); login.setVisible(true);
mainWindow.setUserName(login.getName()); mainWindow.setUserName(login.getName());
mainWindow.setPort(login.getPort()); mainWindow.getConnectionManager().setPort(login.getPort());
login.dispose(); login.dispose();
} }
private void aboutMenuActionPerformed(java.awt.event.ActionEvent evt) { private void aboutMenuActionPerformed(ActionEvent evt) {
About about = new About(); About about = new About();
about.setVisible(true); about.setVisible(true);
} }
/** public void addPluginMenu(MenuCategory category, JMenuItem menu){
* Adiciona um menu fornecido por um plugin à seção de plugins da barra System.out.println("addPluginMenu");
* de menus. categories.get(category).add(menu);
*
* @param menu menu criado pelo plugin.
*/
public void addPlugin(JMenu menu){
this.pluginMenu.add(menu);
} }
} }
+62 -12
View File
@@ -2,14 +2,16 @@ package pitiupi.GUI;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.List; import java.util.List;
import javax.swing.BorderFactory; import java.util.Map;
import javax.swing.DefaultListModel; import java.util.function.Consumer;
import javax.swing.JList; import javax.swing.*;
import javax.swing.JPanel;
import javax.swing.JScrollPane; import pitiupi.models.Menus.MenuCategory;
import javax.swing.SwingUtilities; import pitiupi.models.User;
import pitiupi.control.PeerInfo;
import pitiupi.control.PeerListener; import pitiupi.control.PeerListener;
/** /**
@@ -20,10 +22,16 @@ import pitiupi.control.PeerListener;
*/ */
public class OnlineUsersPanel extends JPanel implements PeerListener { public class OnlineUsersPanel extends JPanel implements PeerListener {
private final DefaultListModel<String> listModel; private final DefaultListModel<User> listModel;
private final JList<String> userList; private final JList<User> userList;
JPopupMenu menu;
private Map<MenuCategory, JMenu> categories;
public OnlineUsersPanel() { public OnlineUsersPanel() {
menu = new JPopupMenu();
categories = new HashMap<>();
initMenu();
setLayout(new BorderLayout()); setLayout(new BorderLayout());
setBorder(BorderFactory.createTitledBorder("Online")); setBorder(BorderFactory.createTitledBorder("Online"));
setPreferredSize(new Dimension(160, 0)); setPreferredSize(new Dimension(160, 0));
@@ -31,6 +39,48 @@ public class OnlineUsersPanel extends JPanel implements PeerListener {
listModel = new DefaultListModel<>(); listModel = new DefaultListModel<>();
userList = new JList<>(listModel); userList = new JList<>(listModel);
add(new JScrollPane(userList), BorderLayout.CENTER); add(new JScrollPane(userList), BorderLayout.CENTER);
setupUserListActions();
}
private void initMenu() {
for (MenuCategory category : MenuCategory.values()) {
JMenu newCategory = new JMenu(category.toString());
menu.add(newCategory);
categories.put(category, newCategory);
}
}
private void setupUserListActions() {
userList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int index = userList.locationToIndex(e.getPoint());
if (index == -1) {
return;
}
userList.setSelectedIndex(index);
menu.show(userList, e.getX(), e.getY());
}
});
}
public void initUnicastMenu(JMenuItem menuItem, Consumer<User> action) {
menuItem.addActionListener(e -> {
User user = userList.getSelectedValue();
if (user != null) {
action.accept(user);
}
});
}
public void addUnicastMenu(MenuCategory category, JMenuItem menuItem) {
categories.get(category).add(menuItem);
} }
/** /**
@@ -39,11 +89,11 @@ public class OnlineUsersPanel extends JPanel implements PeerListener {
* @param peers lista atualizada de usuários ativos na rede. * @param peers lista atualizada de usuários ativos na rede.
*/ */
@Override @Override
public void onPeersChanged(List<PeerInfo> peers) { public void onPeersChanged(List<User> peers) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
listModel.clear(); listModel.clear();
for (PeerInfo peer : peers) { for (User peer : peers) {
listModel.addElement(peer.toString()); listModel.addElement(peer);
} }
}); });
} }
@@ -0,0 +1,113 @@
package pitiupi.control;
import pitiupi.GUI.MainWindow;
import pitiupi.control.plugins.PluginManager;
import pitiupi.models.Messages.HeartbeatMessage;
import pitiupi.models.Messages.Message;
import pitiupi.net.tcp.SocketTCP;
import pitiupi.net.tcp.TcpConnection;
import pitiupi.net.udp.SocketUDP;
import java.net.InetAddress;
public class ConnectionManager {
private final String pitiupiIdentifier;
private final PluginManager pluginManager;
private final HeartbeatManager heartbeatManager;
private SocketTCP socketTCP;
private SocketUDP socketUDP;
private String username;
private int port;
public ConnectionManager(MainWindow main, String username, PluginManager pluginManager, int port) {
this.pluginManager = pluginManager;
this.username = username;
this.port = port;
pitiupiIdentifier = "Pitiupi.Alice.2026";
reconnect();
heartbeatManager = new HeartbeatManager(this);
heartbeatManager.start();
}
public TcpConnection connectPluginTcp(InetAddress destinationAdress, String senderId, String receiverId) {
return socketTCP.createPrivateConnection(destinationAdress, senderId, receiverId);
}
public boolean sendTcp(Message message, InetAddress destinationAddress) {
return socketTCP.send(message, destinationAddress);
}
public boolean sendUdpUnicast(Message message, InetAddress destinationAddress) {
try {
socketUDP.sendUnicast(message, destinationAddress);
return true;
} catch (Exception ex) {
return false;
}
}
public boolean sendUdpMulticast(Message message) {
try {
socketUDP.sendMulticast(message);
return true;
} catch (Exception e) {
System.err.println("Error sending UDP Multicast");
}
return false;
}
public void broadcastLocal(byte[] message) {
pluginManager.handleMessage(message);
}
public void addPeerListener(PeerListener peerListener) {
heartbeatManager.addPeerListener(peerListener);
}
public void receiveHeartbeat(HeartbeatMessage message, InetAddress destinationAddress) {
heartbeatManager.receiveHeartbeat(message, destinationAddress);
}
public void setPort(int port) {
this.port = port;
reconnect();
}
public int getPort() {
return port;
}
public String getIdentifier(){
return this.pitiupiIdentifier;
}
private void reconnect() {
if (this.socketUDP != null) {
this.socketUDP.close();
}
if (this.socketTCP != null) {
this.socketTCP.close();
}
this.socketUDP = new SocketUDP(this, pluginManager);
this.socketTCP = new SocketTCP(this, pluginManager);
this.socketUDP.start();
this.socketTCP.start();
}
public String getUserName() {
return this.username;
}
public void setUserName(String userName) {
this.username = userName;
}
}
@@ -0,0 +1,95 @@
package pitiupi.control;
import pitiupi.GUI.MainWindow;
import pitiupi.GUI.MenuBar;
import pitiupi.GUI.OnlineUsersPanel;
import pitiupi.models.Menus.*;
import pitiupi.models.Menus.MulticastMenus.MulticastActionMenu;
import pitiupi.models.Menus.MulticastMenus.MulticastMenu;
import pitiupi.models.Menus.MulticastMenus.MulticastSubMenu;
import pitiupi.models.Menus.UnicastMenus.UnicastActionMenu;
import pitiupi.models.Menus.UnicastMenus.UnicastMenu;
import pitiupi.models.Menus.UnicastMenus.UnicastSubMenu;
import javax.swing.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class GuiManager {
private MainWindow mainWindow;
private MenuBar menuBar;
private OnlineUsersPanel onlineUsersPanel;
private Logger logger;
public GuiManager(MainWindow mainWindow, MenuBar menuBar, OnlineUsersPanel onlineUsersPanel) {
this.mainWindow = mainWindow;
this.menuBar = menuBar;
this.onlineUsersPanel = onlineUsersPanel;
this.logger = Logger.getLogger(GuiManager.class.getName());
}
public void createMenu(MenuCategory category, Menu menu) {
System.out.println(
menu.getClass().getSuperclass().getSuperclass()
);
System.out.println(
MulticastMenu.class
);
System.out.println(
menu.getClass().getSuperclass().getSuperclass()
== MulticastMenu.class
);
try {
JMenuItem newMenu = buildMenu(menu);
if (menu instanceof MulticastMenu) {
menuBar.addPluginMenu(category, newMenu);
}
if (menu instanceof UnicastMenu) {
onlineUsersPanel.addUnicastMenu(category, newMenu);
}
}
catch (IllegalArgumentException e) {
logger.log(Level.WARNING, e.getMessage());
}
}
private JMenuItem buildMenu(Menu menu) {
if (menu instanceof MulticastSubMenu multicastSubMenu) {
JMenu newMenu = new JMenu(menu.getText());
for (Menu child : multicastSubMenu.getMenus()) {
newMenu.add(buildMenu(child));
}
return newMenu;
}
if (menu instanceof UnicastSubMenu unicastSubMenu) {
JMenu newMenu = new JMenu(menu.getText());
for (Menu child : unicastSubMenu.getMenus()) {
newMenu.add(buildMenu(child));
}
return newMenu;
}
if (menu instanceof MulticastActionMenu multicastActionMenu) {
JMenuItem newMenu = new JMenuItem(menu.getText());
newMenu.addActionListener(e -> multicastActionMenu.onClick());
return newMenu;
}
if (menu instanceof UnicastActionMenu unicastActionMenu) {
JMenuItem newMenu = new JMenuItem(menu.getText());
onlineUsersPanel.initUnicastMenu(newMenu, unicastActionMenu::onClick);
return newMenu;
}
throw new IllegalArgumentException("Invalid menu type");
}
}
@@ -1,6 +1,5 @@
package pitiupi.control; package pitiupi.control;
import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -10,10 +9,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger; import pitiupi.models.User;
import pitiupi.GUI.MainWindow; import pitiupi.models.Messages.HeartbeatMessage;
import pitiupi.net.HeartbeatMessage;
import java.util.UUID; import java.util.UUID;
/** /**
* Gerencia o mecanismo de heartbeat da aplicação. * Gerencia o mecanismo de heartbeat da aplicação.
@@ -28,9 +26,8 @@ public class HeartbeatManager {
private static final long HEARTBEAT_INTERVAL_MS = 5000; private static final long HEARTBEAT_INTERVAL_MS = 5000;
private static final long PEER_TIMEOUT_MS = 15000; // ~3 batimentos perdidos private static final long PEER_TIMEOUT_MS = 15000; // ~3 batimentos perdidos
private final ConnectionManager connectionManager;
private final MainWindow mainWindow; private final Map<String, User> peers = new ConcurrentHashMap<>();
private final Map<String, PeerInfo> peers = new ConcurrentHashMap<>();
private final List<PeerListener> listeners = new CopyOnWriteArrayList<>(); private final List<PeerListener> listeners = new CopyOnWriteArrayList<>();
private ScheduledExecutorService scheduler; private ScheduledExecutorService scheduler;
private final String instanceId = UUID.randomUUID().toString(); private final String instanceId = UUID.randomUUID().toString();
@@ -38,11 +35,9 @@ public class HeartbeatManager {
/** /**
* Cria um gerenciador de heartbeat associado à janela principal da aplicação. * Cria um gerenciador de heartbeat associado à janela principal da aplicação.
* *
* @param mainWindow janela principal utilizada para obter o nome do usuário
* e o socket de comunicação.
*/ */
public HeartbeatManager(MainWindow mainWindow) { public HeartbeatManager(ConnectionManager connectionManager) {
this.mainWindow = mainWindow; this.connectionManager = connectionManager;
} }
/** /**
@@ -64,12 +59,8 @@ public class HeartbeatManager {
} }
private void announce() { private void announce() {
try { HeartbeatMessage msg = new HeartbeatMessage(connectionManager.getUserName(), instanceId);
HeartbeatMessage msg = new HeartbeatMessage(mainWindow.getUserName(), instanceId); connectionManager.sendUdpMulticast(msg);
mainWindow.getSocketUDP().sendMulticast(msg);
} catch (IOException ex) {
Logger.getLogger(HeartbeatManager.class.getName()).log(Level.WARNING, "Falha ao enviar heartbeat", ex);
}
} }
// Chamado pelo Socket quando um pacote é identificado como heartbeat // Chamado pelo Socket quando um pacote é identificado como heartbeat
@@ -94,7 +85,7 @@ public class HeartbeatManager {
String key = msg.getInstanceId(); String key = msg.getInstanceId();
boolean isNew = !peers.containsKey(key); boolean isNew = !peers.containsKey(key);
PeerInfo peer = peers.computeIfAbsent(key, k -> new PeerInfo(msg.getUserName(), from, msg.getTimestamp())); User peer = peers.computeIfAbsent(key, k -> new User(msg.getUserName(), from, msg.getTimestamp()));
peer.touch(System.currentTimeMillis()); peer.touch(System.currentTimeMillis());
if (isNew) { if (isNew) {
@@ -111,7 +102,7 @@ public class HeartbeatManager {
} }
private void notifyListeners() { private void notifyListeners() {
List<PeerInfo> snapshot = new ArrayList<>(peers.values()); List<User> snapshot = new ArrayList<>(peers.values());
for (PeerListener l : listeners) { for (PeerListener l : listeners) {
l.onPeersChanged(snapshot); l.onPeersChanged(snapshot);
} }
@@ -144,7 +135,7 @@ public class HeartbeatManager {
* *
* @return lista contendo os peers atualmente conhecidos. * @return lista contendo os peers atualmente conhecidos.
*/ */
public List<PeerInfo> getPeers() { public List<User> getPeers() {
return new ArrayList<>(peers.values()); return new ArrayList<>(peers.values());
} }
} }
@@ -1,5 +1,7 @@
package pitiupi.control; package pitiupi.control;
import pitiupi.models.User;
import java.util.List; import java.util.List;
/** /**
@@ -17,5 +19,5 @@ public interface PeerListener {
* *
* @param peers lista atualizada de usuários ativos. * @param peers lista atualizada de usuários ativos.
*/ */
void onPeersChanged(List<PeerInfo> peers); void onPeersChanged(List<User> peers);
} }
@@ -1,74 +0,0 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package pitiupi.control;
import java.awt.List;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.ServiceLoader;
import java.util.Vector;
import pitiupi.GUI.MainWindow;
import pitiupi.Main;
import pitiupi.plugin.Plugin;
/**
* Responsável por localizar, carregar e registrar os plugins
* presentes no diretório {@code plugins}.
*
* <p>Os plugins são descobertos utilizando {@link java.util.ServiceLoader}
* e devem estar empacotados como arquivos JAR contendo a configuração
* {@code META-INF/services} correspondente.</p>
* @author flavio
*/
public class PluginLoader {
private final pitiupi.GUI.MainWindow mainWindow;
public PluginLoader(MainWindow main) {
this.mainWindow = main;
}
public void loadPlugins() {
File pluginDir = new File("plugins");
File[] jarFiles = pluginDir.listFiles((dir, name) -> name.endsWith(".jar"));
if (jarFiles == null || jarFiles.length == 0) {
System.out.println("No plugins found in the " + pluginDir.getAbsolutePath() + " directory.");
return;
}
try {
// 2. Convert jar file paths into URLs
ArrayList urls = new ArrayList();
for (File file : jarFiles) {
urls.add(file.toURI().toURL());
System.out.println("Discovered plugin package: " + file.getName());
}
// 3. Create a class loader isolated from the application's default loader
URLClassLoader pluginClassLoader = new URLClassLoader(
(URL[])urls.toArray(new URL[0]),
Main.class.getClassLoader()
);
System.out.println(pluginClassLoader);
// 4. Locate and instantiate all classes implementing the Plugin interface
ServiceLoader<Plugin> serviceLoader = ServiceLoader.load(Plugin.class, pluginClassLoader);
System.out.println("\n--- Executing Discovered Plugins ---" + serviceLoader.findFirst());
for (Plugin plugin : serviceLoader) {
System.out.println("Loading: " + plugin.getName());
plugin.createPlugin(this.mainWindow);
this.mainWindow.getPlugins().add(plugin);
}
} catch (MalformedURLException e) {
System.out.println(e.toString());
}
}
}
@@ -0,0 +1,22 @@
package pitiupi.control.plugins;
import pitiupi.models.Plugin;
class LoadedPlugin {
private final Plugin plugin;
private final ClassLoader classLoader;
public LoadedPlugin(Plugin plugin, ClassLoader classLoader) {
this.plugin = plugin;
this.classLoader = classLoader;
}
public Plugin getPlugin() {
return plugin;
}
public ClassLoader getClassLoader() {
return classLoader;
}
}
@@ -0,0 +1,156 @@
package pitiupi.control.plugins;
import pitiupi.plugin.Pitiupi;
import pitiupi.Main;
import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.models.Messages.Message;
import pitiupi.net.tcp.TcpConnection;
import pitiupi.models.Plugin;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
public class PluginManager {
private final List<LoadedPlugin> loadedPlugins;
private final HashSet<String> loadedJars;
private final Logger logger;
public PluginManager() {
this.loadedPlugins = new ArrayList<>();
this.loadedJars = new HashSet<>();
logger = Logger.getLogger(getClass().getName());
}
public void handleMessage(byte[] msg) {
Message message = null;
for (LoadedPlugin loadedPlugin : loadedPlugins) {
message = deserialize(msg, loadedPlugin.getClassLoader());
if (message != null) break;
}
if (message == null) return;
for (LoadedPlugin loadedPlugin : loadedPlugins) {
Plugin plugin = loadedPlugin.getPlugin();
if (plugin.receiveAllTypes() && message.isPublic()) {
plugin.receiveMessage(message);
continue;
}
for (Class<? extends Message> messageType : plugin.getMessages()) {
if (messageType.isInstance(message)) {
plugin.receiveMessage(message);
break;
}
}
}
}
private Plugin getPlugin(String pluginId) {
for (LoadedPlugin loadedPlugin : loadedPlugins) {
if (loadedPlugin.getPlugin().getIdentifier().equals(pluginId))
return loadedPlugin.getPlugin();
}
return null;
}
public void handleMessageFromConnection(TcpConnection connection, byte[] msg) {
Plugin plugin = getPlugin(connection.getReceiver());
if (plugin == null) return;
plugin.receiveBytes(connection, msg);
}
public boolean handleConnection(TcpConnection connection, String receiverId) {
Plugin plugin = getPlugin(receiverId);
if (plugin == null) return false;
plugin.receivePrivateConnection(connection);
return false;
}
public void handleDisconnection(TcpConnection connection, String receiverId) {
Plugin plugin = getPlugin(receiverId);
if (plugin == null) return;
plugin.onSocketClosed(connection);
}
public void loadPlugins(ConnectionManager connectionManager, GuiManager guiManager) {
File pluginDir = new File("plugins");
File[] jarFiles = pluginDir.listFiles((dir, name) -> name.endsWith(".jar"));
Pitiupi pitiupi = new Pitiupi(connectionManager, guiManager);
if (jarFiles == null || jarFiles.length == 0) {
System.out.println("No plugins found in the " + pluginDir.getAbsolutePath() + " directory.");
return;
}
for (File file : jarFiles) {
if (loadedJars.contains(file.getName())) {
continue;
}
System.out.println("Discovered plugin package: " + file.getName());
try {
URLClassLoader classLoader = new URLClassLoader(
new URL[]{file.toURI().toURL()},
Main.class.getClassLoader()
);
ServiceLoader<Plugin> serviceLoader = ServiceLoader.load(Plugin.class, classLoader);
for (Plugin plugin : serviceLoader) {
System.out.println("Loading: " + plugin.getName());
LoadedPlugin loadedPlugin = new LoadedPlugin(plugin, classLoader);
plugin.createPlugin(pitiupi);
loadedPlugins.add(loadedPlugin);
loadedJars.add(file.getName());
}
} catch (Exception e) {
logger.log(Level.WARNING, "Failed to load plugin " + file.getName(), e);
}
}
}
private Message deserialize(byte[] data, ClassLoader classLoader) {
try {
ByteArrayInputStream input = new ByteArrayInputStream(data);
PluginObjectInputStream objectInput = new PluginObjectInputStream(input, classLoader);
Object object = objectInput.readObject();
if (!(object instanceof Message)) {
return null;
}
return (Message) object;
}
catch (ClassNotFoundException e) {
return null;
}
catch (IOException e) {
logger.log(Level.SEVERE, "Failed to load plugin package", e);
return null;
}
}
}
@@ -0,0 +1,20 @@
package pitiupi.control.plugins;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
public class PluginObjectInputStream extends ObjectInputStream {
private final ClassLoader classLoader;
public PluginObjectInputStream(InputStream input, ClassLoader classLoader) throws IOException {
super(input);
this.classLoader = classLoader;
}
@Override
protected Class<?> resolveClass(ObjectStreamClass description) throws ClassNotFoundException {
return Class.forName(description.getName(), false, classLoader);
}
}
@@ -0,0 +1,5 @@
package pitiupi.models.Menus;
public abstract class Menu {
public abstract String getText();
}
@@ -0,0 +1,7 @@
package pitiupi.models.Menus;
public enum MenuCategory {
Jogos,
Social,
Streaming
}
@@ -0,0 +1,5 @@
package pitiupi.models.Menus.MulticastMenus;
public abstract class MulticastActionMenu extends MulticastMenu{
public abstract void onClick();
}
@@ -0,0 +1,9 @@
package pitiupi.models.Menus.MulticastMenus;
import pitiupi.models.Menus.Menu;
public abstract class MulticastMenu extends Menu {
MulticastMenu() {
super();
}
}
@@ -0,0 +1,7 @@
package pitiupi.models.Menus.MulticastMenus;
import java.util.List;
public abstract class MulticastSubMenu extends MulticastMenu{
public abstract List<MulticastMenu> getMenus();
}
@@ -0,0 +1,7 @@
package pitiupi.models.Menus.UnicastMenus;
import pitiupi.models.User;
public abstract class UnicastActionMenu extends UnicastMenu{
public abstract void onClick(User user);
}
@@ -0,0 +1,8 @@
package pitiupi.models.Menus.UnicastMenus;
import pitiupi.models.Menus.Menu;
import pitiupi.models.User;
public abstract class UnicastMenu extends Menu {
UnicastMenu() {}
}
@@ -0,0 +1,7 @@
package pitiupi.models.Menus.UnicastMenus;
import java.util.List;
public abstract class UnicastSubMenu extends UnicastMenu{
public abstract List<UnicastMenu> getMenus();
}
@@ -1,4 +1,4 @@
package pitiupi.net; package pitiupi.models.Messages;
/** /**
* Mensagem utilizada para sinalizar a presença de um usuário na rede. * Mensagem utilizada para sinalizar a presença de um usuário na rede.
@@ -1,6 +1,6 @@
package pitiupi.net.tcp; package pitiupi.models.Messages;
import pitiupi.net.Message; import pitiupi.net.tcp.TcpConnection;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
@@ -8,12 +8,11 @@ import java.io.ObjectInputStream;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
public class SysInfoMessage extends Message { public class InfoMessage extends Message {
private final InfoType type;
private final SysInfoType type;
private final String message; private final String message;
public SysInfoMessage(String message, SysInfoType type) { public InfoMessage(String message, InfoType type) {
this.message = message; this.message = message;
this.type = type; this.type = type;
} }
@@ -22,29 +21,29 @@ public class SysInfoMessage extends Message {
return message; return message;
} }
public SysInfoType getType() { public InfoType getType() {
return type; return type;
} }
public static void sendSystemInfo(TcpConnection connection, SysInfoType type, String description) { public static void sendSystemInfo(TcpConnection connection, InfoType type, String description) {
try { try {
SysInfoMessage error = new SysInfoMessage(description, type); InfoMessage error = new InfoMessage(description, type);
connection.send(error.toByteArray()); connection.send(error.toByteArray());
} catch (IOException ex) { } catch (IOException ex) {
Logger.getLogger(SysInfoMessage.class.getName()).log(Level.WARNING, null, ex); Logger.getLogger(InfoMessage.class.getName()).log(Level.WARNING, null, ex);
} }
} }
public static SysInfoMessage parseSystemInfo(byte[] bytes) { public static InfoMessage parseSystemInfo(byte[] bytes) {
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis)) { ObjectInputStream ois = new ObjectInputStream(bis)) {
Object object = ois.readObject(); Object object = ois.readObject();
if (object instanceof SysInfoMessage) { if (object instanceof InfoMessage) {
return (SysInfoMessage) object; return (InfoMessage) object;
} }
} catch (IOException | ClassNotFoundException ex) { } catch (IOException | ClassNotFoundException ex) {
Logger.getLogger(SysInfoMessage.class.getName()).log(Level.WARNING, null, ex); Logger.getLogger(InfoMessage.class.getName()).log(Level.WARNING, null, ex);
} }
return null; return null;
} }
@@ -1,6 +1,6 @@
package pitiupi.net.tcp; package pitiupi.models.Messages;
public enum SysInfoType { public enum InfoType {
CONNECTION_REJECTED, CONNECTION_REJECTED,
RECEIVER_NOT_FOUND, RECEIVER_NOT_FOUND,
MESSAGE_REJECTED, MESSAGE_REJECTED,
@@ -3,7 +3,7 @@
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package pitiupi.net; package pitiupi.models.Messages;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@@ -24,6 +24,19 @@ import java.io.Serializable;
* @author flavio * @author flavio
*/ */
public abstract class Message implements Serializable{ public abstract class Message implements Serializable{
private boolean isPublic;
protected Message(){
isPublic = true;
}
protected Message(boolean isPublic){
this.isPublic = isPublic;
}
public boolean isPublic(){
return isPublic;
}
/** /**
* Serializa a mensagem para um vetor de bytes. * Serializa a mensagem para um vetor de bytes.
@@ -2,12 +2,13 @@
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Interface.java to edit this template * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Interface.java to edit this template
*/ */
package pitiupi.plugin; package pitiupi.models;
import pitiupi.plugin.Pitiupi;
import pitiupi.GUI.MainWindow; import pitiupi.models.Messages.Message;
import pitiupi.net.Message;
import pitiupi.net.tcp.TcpConnection; import pitiupi.net.tcp.TcpConnection;
import java.util.List;
/** /**
* Interface base para plugins da aplicação. * Interface base para plugins da aplicação.
* *
@@ -16,28 +17,28 @@ import pitiupi.net.tcp.TcpConnection;
* *
* @author flavio * @author flavio
*/ */
public interface Plugin { public abstract class Plugin {
public abstract String getName();
public String getName(); public abstract String getAuthor();
public String getAuthor(); public abstract String getVersion();
public String getVersion(); public abstract String getIdentifier();
public String getIdentifier();
/** /**
* Inicializa o plugin. * Inicializa o plugin.
* *
* <p>Este método é chamado pela aplicação após o carregamento do plugin.</p> * <p>Este método é chamado pela aplicação após o carregamento do plugin.</p>
* *
* @param window janela principal da aplicação. * @param connectionManager interface com o sistema de conexões da aplicação.
* @param guiManager interface com a janela principal da aplicação
*/ */
public void createPlugin(MainWindow window); public abstract void createPlugin(Pitiupi pitiupi);
/** /**
* Retorna uma mensagem utilizada pelo plugin. * Retorna uma mensagem utilizada pelo plugin.
* *
* @return mensagem criada pelo plugin. * @return mensagem criada pelo plugin.
*/ */
public Message getMessage(); public abstract List<Class<? extends Message>> getMessages();
/** /**
* Recebe uma mensagem enviada pela aplicação. * Recebe uma mensagem enviada pela aplicação.
@@ -47,6 +48,16 @@ public interface Plugin {
* *
* @param message mensagem serializada recebida. * @param message mensagem serializada recebida.
*/ */
public void receiveMessage(byte[] message); public abstract void receiveMessage(Message message);
} public boolean receiveAllTypes() {return false;}
public boolean receivePrivateConnection(TcpConnection connection) {
return false;
}
public boolean receiveBytes(TcpConnection connection, byte[] bytes) {
return false;
}
public boolean onSocketClosed(TcpConnection connection) {
return false;
}
}
@@ -1,4 +1,4 @@
package pitiupi.control; package pitiupi.models;
import java.net.InetAddress; import java.net.InetAddress;
/** /**
@@ -10,13 +10,13 @@ import java.net.InetAddress;
* *
* @author tony * @author tony
*/ */
public class PeerInfo { public class User {
private final String userName; private final String userName;
private final InetAddress address; private final InetAddress address;
private volatile long lastSeen; private volatile long lastSeen;
public PeerInfo(String userName, InetAddress address, long lastSeen) { public User(String userName, InetAddress address, long lastSeen) {
this.userName = userName; this.userName = userName;
this.address = address; this.address = address;
this.lastSeen = lastSeen; this.lastSeen = lastSeen;
@@ -1,6 +1,8 @@
package pitiupi.net.tcp; package pitiupi.net.tcp;
import pitiupi.net.Message; import pitiupi.models.Messages.Message;
import pitiupi.models.Messages.InfoMessage;
import pitiupi.models.Messages.InfoType;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
@@ -24,7 +26,7 @@ import java.util.logging.Logger;
* a utilizar o timeout padrão definido por {@link #STANDARD_TIMEOUT}.</p> * a utilizar o timeout padrão definido por {@link #STANDARD_TIMEOUT}.</p>
*/ */
public class ConnectionFactory { public class ConnectionFactory {
private static final int HANDSHAKE_TIMEOUT = 5000; private static final int HANDSHAKE_TIMEOUT = 1000;
private static final int STANDARD_TIMEOUT = 300_000; private static final int STANDARD_TIMEOUT = 300_000;
/** /**
@@ -64,7 +66,9 @@ public class ConnectionFactory {
connection.send(handshake.toByteArray()); connection.send(handshake.toByteArray());
HandshakeMessage handshakeResponse = validateConnection(connection); HandshakeMessage handshakeResponse = validateConnection(connection);
if (handshakeResponse == null) return null; if (handshakeResponse == null) {
return null;
}
connection.setTimeout(STANDARD_TIMEOUT); connection.setTimeout(STANDARD_TIMEOUT);
@@ -102,7 +106,6 @@ public class ConnectionFactory {
if (handshake == null) return null; if (handshake == null) return null;
connection.send(handshake.createResponse().toByteArray()); connection.send(handshake.createResponse().toByteArray());
connection.setSender(handshake.getSender()); connection.setSender(handshake.getSender());
@@ -151,7 +154,7 @@ public class ConnectionFactory {
Object object = ois.readObject(); Object object = ois.readObject();
if (object instanceof Message message) { if (object instanceof Message message) {
SysInfoMessage sysInfo = SysInfoMessage.parseSystemInfo(bytes); InfoMessage sysInfo = InfoMessage.parseSystemInfo(bytes);
if (sysInfo != null) return null; if (sysInfo != null) return null;
@@ -160,25 +163,25 @@ public class ConnectionFactory {
} }
} }
SysInfoMessage.sendSystemInfo( InfoMessage.sendSystemInfo(
connection, connection,
SysInfoType.CONNECTION_REJECTED, InfoType.CONNECTION_REJECTED,
"The message was not from an allowed type." "The message was not from an allowed type."
); );
} }
catch (SocketTimeoutException ex) { catch (SocketTimeoutException ex) {
SysInfoMessage.sendSystemInfo( InfoMessage.sendSystemInfo(
connection, connection,
SysInfoType.CONNECTION_REJECTED, InfoType.CONNECTION_REJECTED,
"No handshake was received within the allowed time." "No handshake was received within the allowed time."
); );
} }
catch (ClassNotFoundException | IOException ex) { catch (ClassNotFoundException | IOException ex) {
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(ConnectionFactory.class.getName()).log(Level.SEVERE, null, ex);
SysInfoMessage.sendSystemInfo( InfoMessage.sendSystemInfo(
connection, connection,
SysInfoType.INTERNAL_ERROR, InfoType.INTERNAL_ERROR,
"The server could not process the handshake." "The server could not process the handshake."
); );
} }
@@ -205,17 +208,17 @@ public class ConnectionFactory {
String receiver = handshake.getReceiver(); String receiver = handshake.getReceiver();
if (sender == null || sender.isBlank()) { if (sender == null || sender.isBlank()) {
SysInfoMessage.sendSystemInfo( InfoMessage.sendSystemInfo(
connection, connection,
SysInfoType.CONNECTION_REJECTED, InfoType.CONNECTION_REJECTED,
"Invalid Sender: " + sender + "." "Invalid Sender: " + sender + "."
); );
return false; return false;
} }
if (receiver == null || receiver.isBlank()) { if (receiver == null || receiver.isBlank()) {
SysInfoMessage.sendSystemInfo( InfoMessage.sendSystemInfo(
connection, connection,
SysInfoType.CONNECTION_REJECTED, InfoType.CONNECTION_REJECTED,
"Invalid Receiver: " + receiver + "." "Invalid Receiver: " + receiver + "."
); );
return false; return false;
@@ -1,6 +1,6 @@
package pitiupi.net.tcp; package pitiupi.net.tcp;
import pitiupi.net.Message; import pitiupi.models.Messages.Message;
class HandshakeMessage extends Message { class HandshakeMessage extends Message {
private final boolean isPrivate; private final boolean isPrivate;
+78 -98
View File
@@ -1,11 +1,11 @@
package pitiupi.net.tcp; package pitiupi.net.tcp;
import pitiupi.GUI.MainWindow; import pitiupi.control.ConnectionManager;
import pitiupi.net.Message; import pitiupi.control.plugins.PluginManager;
import pitiupi.plugin.Plugin; import pitiupi.models.Messages.Message;
import pitiupi.plugin.PrivateConnectionPlugin; import pitiupi.models.Messages.InfoMessage;
import pitiupi.models.Messages.InfoType;
import javax.swing.*;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.util.Objects; import java.util.Objects;
@@ -37,11 +37,13 @@ public class SocketTCP extends Thread {
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final PluginManager pluginManager;
private final TcpConnectionManager tcpConnectionManager;
private ServerSocket serverSocket; private ServerSocket serverSocket;
private InetAddress address; private InetAddress address;
private final MainWindow main;
private volatile boolean running = true; private volatile boolean running = true;
/** /**
@@ -51,15 +53,18 @@ public class SocketTCP extends Thread {
* com os peers são estabelecidas sob demanda durante o envio ou aceitas * com os peers são estabelecidas sob demanda durante o envio ou aceitas
* quando iniciadas por outros peers.</p> * quando iniciadas por outros peers.</p>
* *
* @param main janela principal da aplicação, usada para obter a porta, * @param connectionManager interface de conexão da aplicação, usada para obter a porta,
* o identificador local e os plugins disponíveis. * o identificador local e os plugins disponíveis.
*/ */
public SocketTCP(MainWindow main) { public SocketTCP(ConnectionManager connectionManager, PluginManager pluginManager) {
this.main = main; this.connectionManager = connectionManager;
this.pluginManager = pluginManager;
logger = Logger.getLogger(getClass().getName()); logger = Logger.getLogger(getClass().getName());
connectionManager = new ConnectionManager(); tcpConnectionManager = new TcpConnectionManager();
try { try {
serverSocket = new ServerSocket(main.getPort()); serverSocket = new ServerSocket(connectionManager.getPort());
address = InetAddress.getByName(InetAddress.getLocalHost().getHostAddress()); address = InetAddress.getByName(InetAddress.getLocalHost().getHostAddress());
} catch (IOException ex) { } catch (IOException ex) {
System.out.println("There is no socket connection. Sorry."); System.out.println("There is no socket connection. Sorry.");
@@ -78,25 +83,32 @@ public class SocketTCP extends Thread {
* @param msg mensagem que será enviada. * @param msg mensagem que será enviada.
* @param destinationAddress endereço IP do peer que receberá a mensagem. * @param destinationAddress endereço IP do peer que receberá a mensagem.
*/ */
public void send(Message msg, InetAddress destinationAddress) { public boolean send(Message msg, InetAddress destinationAddress) {
try { try {
TcpConnection connection = getPublicConnection(destinationAddress, main.getIdentifier(), main.getIdentifier()); TcpConnection connection = getPublicConnection(
destinationAddress,
connectionManager.getIdentifier(),
connectionManager.getIdentifier()
);
if (connection == null) { if (connection == null) {
System.out.println("Could not establish TCP connection."); System.out.println("Could not establish TCP connection.");
return; return false;
} }
connection.send(msg.toByteArray()); connection.send(msg.toByteArray());
} catch (Exception ex) { } catch (Exception ex) {
System.out.println("Could not send TCP message."); System.out.println("Could not send TCP message.");
System.out.println(ex.toString()); System.out.println(ex.toString());
return false;
} }
return true;
} }
/** /**
* Estabelece uma conexão TCP privada com um plugin específico do peer de destino. * Estabelece uma conexão TCP privada com um plugin específico do peer de destino.
* *
* <p>A conexão é criada como privada e passa a ser gerenciada pelo * <p>A conexão é criada como privada e passa a ser gerenciada pelo
* {@link ConnectionManager}. O método também inicia a tarefa responsável * {@link TcpConnectionManager}. O método também inicia a tarefa responsável
* por receber os dados dessa conexão.</p> * por receber os dados dessa conexão.</p>
* *
* @param destinationAddress endereço IP do peer de destino. * @param destinationAddress endereço IP do peer de destino.
@@ -115,11 +127,11 @@ public class SocketTCP extends Thread {
* Obtém uma conexão TCP pública existente com o peer de destino ou estabelece * Obtém uma conexão TCP pública existente com o peer de destino ou estabelece
* uma nova caso não exista uma conexão válida. * uma nova caso não exista uma conexão válida.
* *
* <p>As conexões públicas são gerenciadas pelo {@link ConnectionManager}. * <p>As conexões públicas são gerenciadas pelo {@link TcpConnectionManager}.
* O método primeiro verifica se já existe uma conexão pública registrada * O método primeiro verifica se já existe uma conexão pública registrada
* para o endereço de destino e, caso ela exista e não esteja fechada, * para o endereço de destino e, caso ela exista e não esteja fechada,
* reutiliza-a. Caso contrário, uma nova conexão pública é estabelecida, * reutiliza-a. Caso contrário, uma nova conexão pública é estabelecida,
* registrada no {@link ConnectionManager} e disponibilizada para uso.</p> * registrada no {@link TcpConnectionManager} e disponibilizada para uso.</p>
* *
* <p>O acesso ao método é sincronizado para evitar que múltiplas threads * <p>O acesso ao método é sincronizado para evitar que múltiplas threads
* estabeleçam simultaneamente conexões públicas para o mesmo peer.</p> * estabeleçam simultaneamente conexões públicas para o mesmo peer.</p>
@@ -133,7 +145,7 @@ public class SocketTCP extends Thread {
private synchronized TcpConnection getPublicConnection(InetAddress destinationAddress, String senderId, String receiverId) { private synchronized TcpConnection getPublicConnection(InetAddress destinationAddress, String senderId, String receiverId) {
TcpConnection connection; TcpConnection connection;
connection = connectionManager.getPublicConnection(destinationAddress); connection = tcpConnectionManager.getPublicConnection(destinationAddress);
if (connection != null && !connection.isClosed()) return connection; if (connection != null && !connection.isClosed()) return connection;
connection = establishOutgoingConnection(destinationAddress, senderId, receiverId, false); connection = establishOutgoingConnection(destinationAddress, senderId, receiverId, false);
@@ -146,7 +158,7 @@ public class SocketTCP extends Thread {
* *
* <p>Cada socket aceito é encaminhado para processamento assíncrono, * <p>Cada socket aceito é encaminhado para processamento assíncrono,
* interpretado como uma {@link TcpConnection}, registrado pelo * interpretado como uma {@link TcpConnection}, registrado pelo
* {@link ConnectionManager} e associado ao receptor adequado.</p> * {@link TcpConnectionManager} e associado ao receptor adequado.</p>
*/ */
private void receive() { private void receive() {
while (running) { while (running) {
@@ -188,7 +200,7 @@ public class SocketTCP extends Thread {
throw new IllegalArgumentException("Connections to the local host are forbidden."); throw new IllegalArgumentException("Connections to the local host are forbidden.");
} }
TcpConnection connection = ConnectionFactory.createConnection(destinationAddress, main.getPort(), sender, receiver, isPrivate); TcpConnection connection = ConnectionFactory.createConnection(destinationAddress, connectionManager.getPort(), sender, receiver, isPrivate);
if (connection == null) return null; if (connection == null) return null;
saveConnection(connection); saveConnection(connection);
@@ -229,10 +241,10 @@ public class SocketTCP extends Thread {
*/ */
private void saveConnection(TcpConnection connection) { private void saveConnection(TcpConnection connection) {
if (connection.isPrivate()) { if (connection.isPrivate()) {
connectionManager.addPrivateConnection(connection); tcpConnectionManager.addPrivateConnection(connection);
} }
else { else {
connectionManager.addPublicConnection(connection); tcpConnectionManager.addPublicConnection(connection);
} }
} }
@@ -254,47 +266,21 @@ public class SocketTCP extends Thread {
return; return;
} }
if (connection.getReceiver().equals(main.getIdentifier())) { if (connection.getReceiver().equals(connectionManager.getIdentifier())) {
systemConnectionReceiver(connection); systemConnectionReceiver(connection);
return; return;
} }
Plugin receiver = findConnectionReceiver(connection); if (pluginManager.handleConnection(connection, connection.getReceiver()))
if (receiver instanceof PrivateConnectionPlugin plugin) { privateConnectionReceiver(connection);
plugin.receivePrivateConnection(connection); else {
InfoMessage.sendSystemInfo(
privateConnectionReceiver(connection, plugin);
}
}
/**
* Localiza o plugin responsável por receber uma conexão privada.
*
* <p>A busca é feita pelo identificador do destinatário informado na
* própria conexão. Caso nenhum plugin correspondente seja encontrado,
* uma mensagem de erro é enviada ao peer remoto.</p>
*
* @param connection conexão privada cujo destinatário será localizado.
* @return o plugin com o identificador correspondente ao destinatário,
* ou {@code null} se nenhum plugin for encontrado.
*/
private Plugin findConnectionReceiver(TcpConnection connection) {
Plugin receiver = null;
for(Plugin plugin : main.getPlugins()) {
if (plugin.getIdentifier().equals(connection.getReceiver())) {
receiver = plugin;
break;
}
}
if (receiver == null) {
SysInfoMessage.sendSystemInfo(
connection, connection,
SysInfoType.RECEIVER_NOT_FOUND, InfoType.RECEIVER_NOT_FOUND,
"This peer does not have the plugin: " + connection.getReceiver() "This peer does not have the plugin: " + connection.getReceiver()
); );
} }
return receiver;
} }
/** /**
@@ -306,7 +292,7 @@ public class SocketTCP extends Thread {
* condição de erro.</p> * condição de erro.</p>
* *
* <p>O método também trata timeouts e erros de I/O, removendo a conexão * <p>O método também trata timeouts e erros de I/O, removendo a conexão
* do {@link ConnectionManager} quando ela deixa de ser utilizável.</p> * do {@link TcpConnectionManager} quando ela deixa de ser utilizável.</p>
* *
* @param connection conexão pública da qual as mensagens serão recebidas. * @param connection conexão pública da qual as mensagens serão recebidas.
*/ */
@@ -315,18 +301,16 @@ public class SocketTCP extends Thread {
try { try {
byte[] bytes = connection.receive(); byte[] bytes = connection.receive();
SysInfoMessage sysMessage = SysInfoMessage.parseSystemInfo(bytes); InfoMessage sysMessage = InfoMessage.parseSystemInfo(bytes);
if (sysMessage == null) { if (sysMessage == null) {
for (Plugin plugin : main.getPlugins()) { pluginManager.handleMessage(bytes);
plugin.receiveMessage(bytes);
}
continue; continue;
} }
switch (sysMessage.getType()) { switch (sysMessage.getType()) {
case CONNECTION_CLOSED, INTERNAL_ERROR -> { case CONNECTION_CLOSED, INTERNAL_ERROR -> {
connectionManager.closeConnection(connection); tcpConnectionManager.closeConnection(connection);
return; return;
} }
case MESSAGE_REJECTED -> { case MESSAGE_REJECTED -> {
@@ -337,12 +321,12 @@ public class SocketTCP extends Thread {
} }
catch (SocketTimeoutException ex) { catch (SocketTimeoutException ex) {
if (connection.hasTimedOut()) { if (connection.hasTimedOut()) {
connectionManager.closeConnection(connection, "Connection timeout"); tcpConnectionManager.closeConnection(connection, "Connection timeout");
return; return;
} }
} }
catch (IOException ex) { catch (IOException ex) {
connectionManager.closeConnection(connection); tcpConnectionManager.closeConnection(connection);
} }
} }
} }
@@ -357,40 +341,37 @@ public class SocketTCP extends Thread {
* do plugin e o fechamento da conexão.</p> * do plugin e o fechamento da conexão.</p>
* *
* @param connection conexão privada da qual os dados serão recebidos. * @param connection conexão privada da qual os dados serão recebidos.
* @param plugin plugin responsável pelo processamento dos dados recebidos.
*/ */
private void privateConnectionReceiver(TcpConnection connection, Plugin plugin) { private void privateConnectionReceiver(TcpConnection connection) {
if (plugin instanceof PrivateConnectionPlugin privatePlugin) { while (running) {
while (running) { try {
try { byte[] message = connection.receive();
byte[] message = connection.receive(); InfoMessage sysMessage = InfoMessage.parseSystemInfo(message);
SysInfoMessage sysMessage = SysInfoMessage.parseSystemInfo(message);
if (sysMessage == null) { if (sysMessage == null) {
privatePlugin.receiveBytes(connection, message); pluginManager.handleMessageFromConnection(connection, message);
continue;
}
switch (sysMessage.getType()) {
case CONNECTION_CLOSED, INTERNAL_ERROR -> {
pluginManager.handleDisconnection(connection, connection.getReceiver());
tcpConnectionManager.closeConnection(connection);
}
default -> {
continue; continue;
} }
switch (sysMessage.getType()) {
case CONNECTION_CLOSED, INTERNAL_ERROR -> {
privatePlugin.onSocketClosed(connection);
connectionManager.closeConnection(connection);
}
default -> {
continue;
}
}
} }
catch (SocketTimeoutException ex) { }
if (connection.hasTimedOut()) { catch (SocketTimeoutException ex) {
connectionManager.closeConnection(connection, "Connection timeout"); if (connection.hasTimedOut()) {
return; tcpConnectionManager.closeConnection(connection, "Connection timeout");
} return;
}
catch (IOException ex) {
connectionManager.closeConnection(connection);
} }
} }
catch (IOException ex) {
tcpConnectionManager.closeConnection(connection);
}
} }
} }
@@ -410,7 +391,7 @@ public class SocketTCP extends Thread {
try (ByteArrayInputStream bis = new ByteArrayInputStream(message); try (ByteArrayInputStream bis = new ByteArrayInputStream(message);
ObjectInputStream ois = new ObjectInputStream(bis)) { ObjectInputStream ois = new ObjectInputStream(bis)) {
SysInfoMessage sysMessage = SysInfoMessage.parseSystemInfo(message); InfoMessage sysMessage = InfoMessage.parseSystemInfo(message);
if (sysMessage != null) continue; if (sysMessage != null) continue;
//TODO: Ainda não existem mensagens trocadas entre o sistema. //TODO: Ainda não existem mensagens trocadas entre o sistema.
@@ -421,7 +402,7 @@ public class SocketTCP extends Thread {
if (connection.hasTimedOut()) return; if (connection.hasTimedOut()) return;
} }
catch (IOException ex) { catch (IOException ex) {
connectionManager.closeConnection(connection); tcpConnectionManager.closeConnection(connection);
} }
} }
} }
@@ -430,19 +411,18 @@ public class SocketTCP extends Thread {
* Encerra uma conexão TCP e registra o motivo informado. * Encerra uma conexão TCP e registra o motivo informado.
* *
* <p>Quando nenhuma descrição é fornecida, é utilizado um motivo padrão. * <p>Quando nenhuma descrição é fornecida, é utilizado um motivo padrão.
* O fechamento efetivo da conexão é delegado ao {@link ConnectionManager}.</p> * O fechamento efetivo da conexão é delegado ao {@link TcpConnectionManager}.</p>
* *
* @param connection conexão que será encerrada. * @param connection conexão que será encerrada.
* @param description motivo do encerramento; pode ser {@code null}. * @param description motivo do encerramento; pode ser {@code null}.
*/ */
public void disconnect(TcpConnection connection, String description) { public void disconnect(TcpConnection connection, String description) {
connectionManager.closeConnection( tcpConnectionManager.closeConnection(
connection, connection,
Objects.requireNonNullElse(description, "No reason provided") Objects.requireNonNullElse(description, "No reason provided")
); );
} }
@Override @Override
public void run() { public void run() {
receive(); receive();
@@ -469,7 +449,7 @@ public class SocketTCP extends Thread {
} }
} }
connectionManager.closeAllConnections("The peer must have closed the application or changed ports"); tcpConnectionManager.closeAllConnections("The peer must have closed the application or changed ports");
connectionExecutor.shutdownNow(); connectionExecutor.shutdownNow();
} }
@@ -1,7 +1,9 @@
package pitiupi.net.tcp; package pitiupi.net.tcp;
import pitiupi.control.PeerInfo; import pitiupi.models.Messages.InfoType;
import pitiupi.models.User;
import pitiupi.control.PeerListener; import pitiupi.control.PeerListener;
import pitiupi.models.Messages.InfoMessage;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
@@ -27,7 +29,7 @@ import java.util.stream.Collectors;
* peers ativos para encerrar conexões associadas a peers que deixaram de * peers ativos para encerrar conexões associadas a peers que deixaram de
* estar disponíveis.</p> * estar disponíveis.</p>
*/ */
class ConnectionManager implements PeerListener { class TcpConnectionManager implements PeerListener {
private final Logger logger; private final Logger logger;
private final Map<InetAddress, TcpConnection> publicConnections; private final Map<InetAddress, TcpConnection> publicConnections;
private final Map<InetAddress, List<TcpConnection>> privateConnections; private final Map<InetAddress, List<TcpConnection>> privateConnections;
@@ -35,7 +37,7 @@ class ConnectionManager implements PeerListener {
/** /**
* Cria um gerenciador de conexões TCP vazio. * Cria um gerenciador de conexões TCP vazio.
*/ */
public ConnectionManager() { public TcpConnectionManager() {
publicConnections = new ConcurrentHashMap<>(); publicConnections = new ConcurrentHashMap<>();
privateConnections = new ConcurrentHashMap<>(); privateConnections = new ConcurrentHashMap<>();
@@ -65,7 +67,7 @@ class ConnectionManager implements PeerListener {
*/ */
public void addPrivateConnection(TcpConnection connection) { public void addPrivateConnection(TcpConnection connection) {
List<TcpConnection> connections = List<TcpConnection> connections =
privateConnections.computeIfAbsent(connection.getAddress(), _ -> new ArrayList<>()); privateConnections.computeIfAbsent(connection.getAddress(), e -> new ArrayList<>());
synchronized (connections) { synchronized (connections) {
connections.add(connection); connections.add(connection);
@@ -99,7 +101,7 @@ class ConnectionManager implements PeerListener {
* @param reason motivo do encerramento que será informado ao peer. * @param reason motivo do encerramento que será informado ao peer.
*/ */
public void closeConnection(TcpConnection connection, String reason) { public void closeConnection(TcpConnection connection, String reason) {
SysInfoMessage.sendSystemInfo(connection, SysInfoType.CONNECTION_CLOSED, reason); InfoMessage.sendSystemInfo(connection, InfoType.CONNECTION_CLOSED, reason);
closeConnection(connection); closeConnection(connection);
} }
@@ -153,7 +155,7 @@ class ConnectionManager implements PeerListener {
public void closeByAdress(InetAddress address, String reason) { public void closeByAdress(InetAddress address, String reason) {
List<TcpConnection> connections = privateConnections.get(address); List<TcpConnection> connections = privateConnections.get(address);
for (TcpConnection connection : connections) { for (TcpConnection connection : connections) {
SysInfoMessage.sendSystemInfo(connection, SysInfoType.CONNECTION_CLOSED, reason); InfoMessage.sendSystemInfo(connection, InfoType.CONNECTION_CLOSED, reason);
} }
closeByAdress(address); closeByAdress(address);
} }
@@ -205,7 +207,7 @@ class ConnectionManager implements PeerListener {
public void closeAllConnections(String reason) { public void closeAllConnections(String reason) {
for (List<TcpConnection> connections : privateConnections.values()) { for (List<TcpConnection> connections : privateConnections.values()) {
for (TcpConnection connection : connections) { for (TcpConnection connection : connections) {
SysInfoMessage.sendSystemInfo(connection, SysInfoType.CONNECTION_CLOSED, reason); InfoMessage.sendSystemInfo(connection, InfoType.CONNECTION_CLOSED, reason);
} }
} }
closeAllConnections(); closeAllConnections();
@@ -245,8 +247,8 @@ class ConnectionManager implements PeerListener {
*/ */
@Override @Override
public void onPeersChanged(List<PeerInfo> peers) { public void onPeersChanged(List<User> peers) {
Set<InetAddress> activePeers = peers.stream().map(PeerInfo::getAddress).collect(Collectors.toSet()); Set<InetAddress> activePeers = peers.stream().map(User::getAddress).collect(Collectors.toSet());
for (InetAddress address : publicConnections.keySet()) { for (InetAddress address : publicConnections.keySet()) {
if (!activePeers.contains(address)) { if (!activePeers.contains(address)) {
+29 -26
View File
@@ -5,7 +5,6 @@
*/ */
package pitiupi.net.udp; package pitiupi.net.udp;
import pitiupi.GUI.MainWindow;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInput; import java.io.ObjectInput;
@@ -14,9 +13,10 @@ import java.net.*;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import pitiupi.net.HeartbeatMessage; import pitiupi.control.ConnectionManager;
import pitiupi.net.Message; import pitiupi.control.plugins.PluginManager;
import pitiupi.plugin.Plugin; import pitiupi.models.Messages.HeartbeatMessage;
import pitiupi.models.Messages.Message;
/** /**
* Gerencia a comunicação UDP multicast e unicast da aplicação. * Gerencia a comunicação UDP multicast e unicast da aplicação.
@@ -37,7 +37,10 @@ public class SocketUDP extends Thread {
private MulticastSocket multicastSocket; private MulticastSocket multicastSocket;
private InetAddress address; private InetAddress address;
private MainWindow main;
private final ConnectionManager connectionManager;
private final PluginManager pluginManager;
private volatile boolean running = true; private volatile boolean running = true;
public final static String INET_ADDR = "224.0.0.3"; public final static String INET_ADDR = "224.0.0.3";
@@ -47,22 +50,26 @@ public class SocketUDP extends Thread {
* <p>O socket é associado à porta da aplicação e ingressa no grupo * <p>O socket é associado à porta da aplicação e ingressa no grupo
* multicast configurado.</p> * multicast configurado.</p>
* *
* @param main janela principal da aplicação. * @param connectionManager interface que gerencia toda conexão da aplicação.
* @param pluginManager interface que gerencia os plugins da conexão.
*/ */
public SocketUDP(MainWindow main) { public SocketUDP(ConnectionManager connectionManager, PluginManager pluginManager) {
this.main = main; this.connectionManager = connectionManager;
this.pluginManager = pluginManager;
try { try {
this.address = InetAddress.getByName(SocketUDP.INET_ADDR); this.address = InetAddress.getByName(SocketUDP.INET_ADDR);
} catch (UnknownHostException ex) {
System.getLogger(SocketUDP.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex); multicastSocket = new MulticastSocket(connectionManager.getPort());
}
try {
multicastSocket = new MulticastSocket(this.main.getPort());
multicastSocket.setSendBufferSize(256000); multicastSocket.setSendBufferSize(256000);
multicastSocket.setReceiveBufferSize(256000); multicastSocket.setReceiveBufferSize(256000);
multicastSocket.setReuseAddress(true); multicastSocket.setReuseAddress(true);
multicastSocket.joinGroup(address); multicastSocket.joinGroup(address);
} catch (IOException ex) { }
catch (UnknownHostException ex) {
System.getLogger(SocketUDP.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
}
catch (IOException ex) {
System.out.println("There is no socket connection. Sorry."); System.out.println("There is no socket connection. Sorry.");
System.out.println(ex); System.out.println(ex);
} }
@@ -71,22 +78,22 @@ public class SocketUDP extends Thread {
/** /**
* Envia uma mensagem para o grupo multicast da aplicação. * Envia uma mensagem para o grupo multicast da aplicação.
* *
* @param msg mensagem serializada a ser enviada. * @param msg mensagem a ser enviada.
* @throws IOException caso ocorra um erro durante o envio. * @throws IOException caso ocorra um erro durante o envio.
*/ */
public void sendMulticast(Message msg) throws IOException { public void sendMulticast(Message msg) throws IOException {
send(msg, address); send(msg.toByteArray(), this.address);
} }
/** /**
* Envia uma mensagem diretamente para um peer utilizando UDP. * Envia uma mensagem diretamente para um peer utilizando UDP.
* *
* @param msg mensagem serializada a ser enviada. * @param msg mensagem a ser enviada.
* @param destinationAddress endereço IP do peer destinatário. * @param destinationAddress endereço IP do peer destinatário.
* @throws IOException caso ocorra um erro durante o envio. * @throws IOException caso ocorra um erro durante o envio.
*/ */
public void sendUnicast(Message msg, InetAddress destinationAddress) throws IOException { public void sendUnicast(Message msg, InetAddress destinationAddress) throws IOException {
send(msg, destinationAddress); send(msg.toByteArray(), destinationAddress);
} }
/** /**
@@ -96,10 +103,9 @@ public class SocketUDP extends Thread {
* @param destinationAddress endereço de destino. * @param destinationAddress endereço de destino.
* @throws IOException caso ocorra um erro durante o envio. * @throws IOException caso ocorra um erro durante o envio.
*/ */
private void send(Message msg, InetAddress destinationAddress) throws IOException { private void send(byte[] msg, InetAddress destinationAddress) throws IOException {
DatagramPacket msgPacket; DatagramPacket msgPacket;
byte[] buf = msg.toByteArray(); msgPacket = new DatagramPacket(msg, msg.length, destinationAddress, connectionManager.getPort());
msgPacket = new DatagramPacket(buf, buf.length, destinationAddress, this.main.getPort());
multicastSocket.send(msgPacket); multicastSocket.send(msgPacket);
} }
@@ -147,14 +153,11 @@ public class SocketUDP extends Thread {
HeartbeatMessage heartbeat = tryParseHeartbeat(msgPacket.getData()); HeartbeatMessage heartbeat = tryParseHeartbeat(msgPacket.getData());
if (heartbeat != null) { if (heartbeat != null) {
main.getHeartbeatManager().receiveHeartbeat(heartbeat, msgPacket.getAddress()); connectionManager.receiveHeartbeat(heartbeat, msgPacket.getAddress());
continue; continue;
} }
for (Plugin plugin : main.getPlugins()) { pluginManager.handleMessage(msgPacket.getData());
System.out.println(msgPacket.getAddress().toString());
plugin.receiveMessage(msgPacket.getData());
}
} }
} }
+49
View File
@@ -0,0 +1,49 @@
package pitiupi.plugin;
import pitiupi.control.ConnectionManager;
import pitiupi.control.GuiManager;
import pitiupi.models.Menus.Menu;
import pitiupi.models.Menus.MenuCategory;
import pitiupi.models.Messages.Message;
import pitiupi.net.tcp.TcpConnection;
import java.net.InetAddress;
public class Pitiupi {
private final ConnectionManager connectionManager;
private final GuiManager guiManager;
public Pitiupi(ConnectionManager connectionManager, GuiManager guiManager) {
this.connectionManager = connectionManager;
this.guiManager = guiManager;
}
public boolean sendUdpMulticast(Message message) {
return connectionManager.sendUdpMulticast(message);
}
public boolean sendUdpUnicast(Message message, InetAddress address) {
return connectionManager.sendUdpUnicast(message, address);
}
public boolean sendTcp(Message message, InetAddress address) {
return connectionManager.sendTcp(message, address);
}
public TcpConnection connectTcp(InetAddress address, String senderId, String receiverId) {
return connectionManager.connectPluginTcp(address, senderId, receiverId);
}
public String getUserName() {
return connectionManager.getUserName();
}
public int getPort() {
return connectionManager.getPort();
}
public void createMenu(MenuCategory category, Menu menu) {
guiManager.createMenu(category, menu);
}
}
@@ -1,9 +0,0 @@
package pitiupi.plugin;
import pitiupi.net.tcp.TcpConnection;
public interface PrivateConnectionPlugin extends Plugin {
public void receivePrivateConnection(TcpConnection connection);
public void receiveBytes(TcpConnection connection, byte[] bytes);
public void onSocketClosed(TcpConnection connection);
}