This commit is contained in:
GustavoHMDS committed 2026-09-28 10:33:09 -03:00
1 parent b5bbd1df2d
commit 7168431bcd
9 files changed
+339 -244

No files matched your search

+27 -92
View File
@@ -5,13 +5,11 @@
*/ */
package pitiupi.GUI; package pitiupi.GUI;
import pitiupi.net.tcp.SocketTCP; import pitiupi.control.ConnectionManager;
import pitiupi.net.udp.SocketUDP; import pitiupi.control.PluginManager;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JMenu; import javax.swing.JMenu;
import pitiupi.control.PluginLoader;
import pitiupi.plugin.Plugin;
import pitiupi.control.HeartbeatManager; import pitiupi.control.HeartbeatManager;
/** /**
@@ -36,20 +34,12 @@ public class MainWindow extends javax.swing.JFrame {
private javax.swing.JPanel statusPanel; private javax.swing.JPanel statusPanel;
private javax.swing.JTabbedPane tabbedPane; private javax.swing.JTabbedPane tabbedPane;
private HeartbeatManager heartbeatManager;
private OnlineUsersPanel onlineUsersPanel; private OnlineUsersPanel onlineUsersPanel;
private SocketUDP socketUDP;
private SocketTCP socketTCP;
private String userName; private String userName;
private int port;
private String pitiupiIdentifier; private ConnectionManager connectionManager;
private PluginManager pluginManager;
private PluginLoader pluginManager;
private final List<Plugin> plugins;
/** /**
* Cria a janela principal da aplicação. * Cria a janela principal da aplicação.
@@ -59,22 +49,18 @@ public class MainWindow extends javax.swing.JFrame {
*/ */
public MainWindow(String userName, int port) { public MainWindow(String userName, int port) {
this.userName = userName; this.userName = userName;
this.port = port;
this.pitiupiIdentifier = "Pitiupi.Alice.2026"; pluginManager = new PluginManager(this);
this.menuBar = new MenuBar(this); loadPlugins();
this.plugins = new ArrayList<>();
this.initComponents(); connectionManager = new ConnectionManager(this, pluginManager, port);
menuBar = new MenuBar(this);
initComponents();
} }
private void initComponents() { private void initComponents() {
initGUI();
initSocket();
initHeartbeat();
initPlugins();
this.setSize(new java.awt.Dimension(1024, 768));
}
private void initGUI() {
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("Share My Sheet");
@@ -86,34 +72,19 @@ public class MainWindow extends javax.swing.JFrame {
jScrollPane1.setViewportView(tabbedPane); jScrollPane1.setViewportView(tabbedPane);
statusPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT)); statusPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT));
status.setText("Connected on port " + this.port + " name " + this.userName); updateStatus();
statusPanel.add(status); 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.getContentPane().add(statusPanel, java.awt.BorderLayout.PAGE_END);
this.pack();
}
private void initSocket() {
this.socketUDP = new SocketUDP(this);
this.socketTCP = new SocketTCP(this);
this.socketUDP.start();
this.socketTCP.start();
}
private void initHeartbeat() {
this.heartbeatManager = new HeartbeatManager(this);
this.onlineUsersPanel = new OnlineUsersPanel(); this.onlineUsersPanel = new OnlineUsersPanel();
this.heartbeatManager.addPeerListener(this.onlineUsersPanel); this.connectionManager.addPeerListener(onlineUsersPanel);
this.getContentPane().add(this.onlineUsersPanel, java.awt.BorderLayout.LINE_END); this.getContentPane().add(this.onlineUsersPanel, java.awt.BorderLayout.LINE_END);
this.heartbeatManager.start();
}
private void initPlugins() { this.pack();
this.pluginManager = new PluginLoader(this);
this.pluginManager.loadPlugins(); this.setSize(new java.awt.Dimension(1024, 768));
} }
public void exit() { public void exit() {
@@ -122,65 +93,29 @@ public class MainWindow extends javax.swing.JFrame {
System.exit(0); System.exit(0);
} }
public HeartbeatManager getHeartbeatManager() {
return heartbeatManager;
}
public String getUserName(){ public String getUserName(){
return this.userName; return this.userName;
} }
public String getIdentifier(){ public ConnectionManager getConnectionManager() {
return this.pitiupiIdentifier; return connectionManager;
}
public void loadPlugins() {
pluginManager.loadPlugins();
} }
public void addMenu(JMenu menu) { public void addMenu(JMenu menu) {
this.menuBar.addPlugin(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; this.userName = userName;
updateStatus(); updateStatus();
} }
public void setPort(int port) {
this.port = port;
reconnect();
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 + " name " + this.userName);
} }
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();
}
} }
+1 -1
View File
@@ -80,7 +80,7 @@ public class MenuBar extends JMenuBar {
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();
} }
@@ -0,0 +1,104 @@
package pitiupi.control;
import pitiupi.GUI.MainWindow;
import pitiupi.net.HeartbeatMessage;
import pitiupi.net.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 PluginManager pluginManager;
private HeartbeatManager heartbeatManager;
private SocketTCP socketTCP;
private SocketUDP socketUDP;
private int port;
public ConnectionManager(MainWindow main, PluginManager pluginManager, int port) {
this.pluginManager = pluginManager;
this.port = port;
pitiupiIdentifier = "Pitiupi.Alice.2026";
reconnect();
heartbeatManager = new HeartbeatManager(main, 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 ex) {
return false;
}
}
public void broadcastLocal(Message message) {
pluginManager.handlePublicMessage(message);
}
public boolean sendLocal(byte[] message, String receiverId) {
return pluginManager.handlePrivateMessage(message, receiverId);
}
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();
}
}
@@ -30,6 +30,7 @@ public class HeartbeatManager {
private static final long PEER_TIMEOUT_MS = 15000; // ~3 batimentos perdidos private static final long PEER_TIMEOUT_MS = 15000; // ~3 batimentos perdidos
private final MainWindow mainWindow; private final MainWindow mainWindow;
private final ConnectionManager connectionManager;
private final Map<String, PeerInfo> 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;
@@ -41,8 +42,9 @@ public class HeartbeatManager {
* @param mainWindow janela principal utilizada para obter o nome do usuário * @param mainWindow janela principal utilizada para obter o nome do usuário
* e o socket de comunicação. * e o socket de comunicação.
*/ */
public HeartbeatManager(MainWindow mainWindow) { public HeartbeatManager(MainWindow mainWindow, ConnectionManager connectionManager) {
this.mainWindow = mainWindow; this.mainWindow = mainWindow;
this.connectionManager = connectionManager;
} }
/** /**
@@ -64,12 +66,8 @@ public class HeartbeatManager {
} }
private void announce() { private void announce() {
try { HeartbeatMessage msg = new HeartbeatMessage(mainWindow.getUserName(), instanceId);
HeartbeatMessage msg = new HeartbeatMessage(mainWindow.getUserName(), instanceId); connectionManager.sendUdpMulticast(msg);
mainWindow.getSocketUDP().sendMulticast(msg.toByteArray());
} 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
+26 -27
View File
@@ -4,14 +4,12 @@
*/ */
package pitiupi.control; package pitiupi.control;
import java.awt.List;
import java.io.File; import java.io.File;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.ArrayList; import java.util.*;
import java.util.ServiceLoader;
import java.util.Vector;
import pitiupi.GUI.MainWindow; import pitiupi.GUI.MainWindow;
import pitiupi.Main; import pitiupi.Main;
import pitiupi.plugin.Plugin; import pitiupi.plugin.Plugin;
@@ -33,7 +31,7 @@ public class PluginLoader {
this.mainWindow = main; this.mainWindow = main;
} }
public void loadPlugins() { public void loadPlugins(List<Plugin> plugins, HashSet<String> loadedJars) {
File pluginDir = new File("plugins"); File pluginDir = new File("plugins");
File[] jarFiles = pluginDir.listFiles((dir, name) -> name.endsWith(".jar")); File[] jarFiles = pluginDir.listFiles((dir, name) -> name.endsWith(".jar"));
@@ -42,33 +40,34 @@ public class PluginLoader {
return; return;
} }
try { for (File file : jarFiles) {
// 2. Convert jar file paths into URLs
ArrayList urls = new ArrayList(); if (loadedJars.contains(file.getName())) {
for (File file : jarFiles) { continue;
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 System.out.println("Discovered plugin package: " + file.getName());
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 try {
ServiceLoader<Plugin> serviceLoader = ServiceLoader.load(Plugin.class, pluginClassLoader); URLClassLoader classLoader = new URLClassLoader(
new URL[]{file.toURI().toURL()},
Main.class.getClassLoader()
);
System.out.println("\n--- Executing Discovered Plugins ---" + serviceLoader.findFirst()); ServiceLoader<Plugin> serviceLoader = ServiceLoader.load(Plugin.class, classLoader);
for (Plugin plugin : serviceLoader) {
System.out.println("Loading: " + plugin.getName()); for (Plugin plugin : serviceLoader) {
plugin.createPlugin(this.mainWindow); System.out.println("Loading: " + plugin.getName());
this.mainWindow.getPlugins().add(plugin);
plugin.createPlugin(this.mainWindow);
plugins.add(plugin);
loadedJars.add(file.getName());
}
} catch (Exception e) {
System.out.println("Failed to load plugin " + file.getName());
e.printStackTrace();
} }
} catch (MalformedURLException e) {
System.out.println(e.toString());
} }
} }
} }
@@ -0,0 +1,71 @@
package pitiupi.control;
import pitiupi.GUI.MainWindow;
import pitiupi.net.Message;
import pitiupi.net.tcp.TcpConnection;
import pitiupi.plugin.Plugin;
import pitiupi.plugin.PrivateConnectionPlugin;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class PluginManager {
private PluginLoader pluginLoader;
private final List<Plugin> plugins;
private final HashSet<String> loadedJars;
public PluginManager(MainWindow main) {
this.pluginLoader = new PluginLoader(main);
this.plugins = new ArrayList<>();
this.loadedJars = new HashSet<>();
}
public void loadPlugins() {
pluginLoader.loadPlugins(plugins, loadedJars);
}
public void handlePublicMessage(Message msg) {
for (Plugin plugin : plugins) {
plugin.receiveMessage(msg);
}
}
private Plugin getPlugin(String pluginId) {
for (Plugin plugin : plugins) {
if (plugin.getIdentifier().equals(pluginId)) return plugin;
}
return null;
}
public boolean handlePrivateMessage(byte[] msg, String receiverId) {
Plugin plugin = getPlugin(receiverId);
if (plugin == null) return false;
if (plugin instanceof PrivateConnectionPlugin privatePlugin) {
privatePlugin.receiveBytes(msg);
return true;
}
return false;
}
public boolean handleConnection(TcpConnection connection, String receiverId) {
Plugin plugin = getPlugin(receiverId);
if (plugin == null) return false;
if (plugin instanceof PrivateConnectionPlugin privatePlugin) {
privatePlugin.receivePrivateConnection(connection);
return true;
}
return false;
}
public void handleDisconnection(TcpConnection connection, String receiverId) {
Plugin plugin = getPlugin(receiverId);
if (plugin instanceof PrivateConnectionPlugin privatePlugin) {
privatePlugin.onSocketClosed(connection);
}
}
}
+74 -93
View File
@@ -1,11 +1,12 @@
package pitiupi.net.tcp; package pitiupi.net.tcp;
import pitiupi.GUI.MainWindow; import pitiupi.GUI.MainWindow;
import pitiupi.control.ConnectionManager;
import pitiupi.control.PluginManager;
import pitiupi.net.Message; import pitiupi.net.Message;
import pitiupi.plugin.Plugin; import pitiupi.plugin.Plugin;
import pitiupi.plugin.PrivateConnectionPlugin; import pitiupi.plugin.PrivateConnectionPlugin;
import javax.swing.*;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.util.Objects; import java.util.Objects;
@@ -35,12 +36,13 @@ public class SocketTCP extends Thread {
private final ExecutorService connectionExecutor = Executors.newCachedThreadPool(); private final ExecutorService connectionExecutor = Executors.newCachedThreadPool();
private final ConnectionManager connectionManager; private final TcpConnectionManager tcpConnectionManager;
private ServerSocket serverSocket; private ServerSocket serverSocket;
private InetAddress address; private InetAddress address;
private final MainWindow main; private final ConnectionManager connectionManager;
private final PluginManager pluginManager;
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.");
@@ -75,28 +80,36 @@ public class SocketTCP extends Thread {
* do envio. Falhas durante o estabelecimento ou envio são registradas na * do envio. Falhas durante o estabelecimento ou envio são registradas na
* saída padrão e não são propagadas ao chamador.</p> * saída padrão e não são propagadas ao chamador.</p>
* *
* @param msg mensagem serializada em bytes 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(byte[] 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);
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 +128,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 +146,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 +159,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 +201,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 +242,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);
} }
} }
@@ -255,48 +268,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);
if (notify) { else {
plugin.receivePrivateConnection(connection);
}
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( SysInfoMessage.sendSystemInfo(
connection, connection,
SysInfoType.RECEIVER_NOT_FOUND, SysInfoType.RECEIVER_NOT_FOUND,
"This peer does not have the plugin: " + connection.getReceiver() "This peer does not have the plugin: " + connection.getReceiver()
); );
} }
return receiver;
} }
/** /**
@@ -308,7 +294,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.
*/ */
@@ -324,15 +310,13 @@ public class SocketTCP extends Thread {
SysInfoMessage sysMessage = SysInfoMessage.parseSystemInfo(message); SysInfoMessage sysMessage = SysInfoMessage.parseSystemInfo(message);
if (sysMessage == null) { if (sysMessage == null) {
for (Plugin plugin : main.getPlugins()) { pluginManager.handlePublicMessage(message);
plugin.receiveMessage(message);
}
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 -> {
@@ -356,17 +340,17 @@ public class SocketTCP extends Thread {
"class Message not found." "class Message not found."
); );
logger.log(Level.SEVERE, null, ex); logger.log(Level.SEVERE, null, ex);
connectionManager.closeConnection(connection); tcpConnectionManager.closeConnection(connection);
} }
} }
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);
} }
} }
} }
@@ -381,40 +365,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(); SysInfoMessage sysMessage = SysInfoMessage.parseSystemInfo(message);
SysInfoMessage sysMessage = SysInfoMessage.parseSystemInfo(message);
if (sysMessage == null) { if (sysMessage == null) {
privatePlugin.receiveBytes(message); pluginManager.handlePrivateMessage(message, connection.getReceiver());
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);
}
} }
} }
@@ -445,7 +426,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);
} }
} }
} }
@@ -454,13 +435,13 @@ 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")
); );
@@ -493,7 +474,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();
} }
@@ -27,7 +27,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 +35,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<>();
+29 -22
View File
@@ -14,6 +14,8 @@ 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.control.ConnectionManager;
import pitiupi.control.PluginManager;
import pitiupi.net.HeartbeatMessage; import pitiupi.net.HeartbeatMessage;
import pitiupi.net.Message; import pitiupi.net.Message;
import pitiupi.plugin.Plugin; import pitiupi.plugin.Plugin;
@@ -37,7 +39,10 @@ public class SocketUDP extends Thread {
private MulticastSocket multicastSocket; private MulticastSocket multicastSocket;
private InetAddress address; private InetAddress address;
private MainWindow main;
private ConnectionManager connectionManager;
private 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 +52,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 +80,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(byte[] msg) throws IOException { public void sendMulticast(Message msg) throws IOException {
send(msg, this.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(byte[] msg, InetAddress destinationAddress) throws IOException { public void sendUnicast(Message msg, InetAddress destinationAddress) throws IOException {
send(msg, destinationAddress); send(msg.toByteArray(), destinationAddress);
} }
/** /**
@@ -98,7 +107,7 @@ public class SocketUDP extends Thread {
*/ */
private void send(byte[] msg, InetAddress destinationAddress) throws IOException { private void send(byte[] msg, InetAddress destinationAddress) throws IOException {
DatagramPacket msgPacket; DatagramPacket msgPacket;
msgPacket = new DatagramPacket(msg, msg.length, destinationAddress, this.main.getPort()); msgPacket = new DatagramPacket(msg, msg.length, destinationAddress, connectionManager.getPort());
multicastSocket.send(msgPacket); multicastSocket.send(msgPacket);
} }
@@ -145,7 +154,7 @@ 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;
} }
@@ -154,10 +163,8 @@ public class SocketUDP extends Thread {
continue; continue;
} }
for (Plugin plugin : main.getPlugins()) { System.out.println(msgPacket.getAddress().toString());
System.out.println(msgPacket.getAddress().toString()); pluginManager.handlePublicMessage(message);
plugin.receiveMessage(message);
}
} }
} }