/* * This file is part of MazeSolver. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Copyright (c) 2014 MazeSolver * Sergio M. Afonso Fumero <theSkatrak@gmail.com> * Kevin I. Robayna Hernández <kevinirobaynahdez@gmail.com> */ /** * @file MessageManager.java * @date 24/12/2014 */ package es.ull.mazesolver.util; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import es.ull.mazesolver.agent.util.MessageCommunication; import es.ull.mazesolver.agent.util.MessageCommunication.Message; /** * Clase que gestiona el paso de mensajes y la creación de grupos de mensajes * entre agentes. */ public class MessageManager { // Para cada grupo se almacena una lista de mensajes pendientes de transmisión // junto al emisor y una lista de receptores, o agentes suscritos al canal. private HashMap <String, Pair <ArrayList <Pair <MessageCommunication, Message>>, ArrayList <MessageCommunication>>> m_groups; /** * Construye una nueva instancia de la clase. */ public MessageManager () { m_groups = new HashMap <>(); } /** * Suscribe el agente al grupo indicado, de manera que recibe los mensajes * enviados a ese grupo. * * @param agent * Agente que se quiere suscribir. * @param group * Grupo al que suscribir el agente. * @return {@code true} si el agente se añadió al grupo y {@code false} si no * se pudo añadir el agente al grupo. */ public boolean subscribeGroup (MessageCommunication agent, String group) { Pair <ArrayList <Pair <MessageCommunication, Message>>, ArrayList <MessageCommunication>> g = m_groups.get(group); if (g != null) { if (!g.second.contains(agent)) g.second.add(agent); return true; } return false; } /** * Hace que el agente deje de recibir mensajes del grupo especificado. * * @param agent * Agente que quiere eliminar su suscripción. * @param group * Grupo del cual eliminar la suscripción. */ public void unsubscribeGroup (MessageCommunication agent, String group) { Pair <ArrayList <Pair <MessageCommunication, Message>>, ArrayList <MessageCommunication>> g = m_groups.get(group); if (g != null) g.second.remove(agent); } /** * Pone un mensaje a la cola de un grupo para ser enviado posteriormente * cuando se llame a {@link #flushMessageQueues}. Que la inserción se realice * satisfactoriamente no implica que el mensaje sea enviado. * * @see #flushMessageQueues * @param sender * Agente que envía el mensaje. * @param group * Grupo al que se está enviando el mensaje. * @param msg * Mensaje que se quiere enviar. * @return {@code true} si el mensaje se ha colocado en la cola * satisfactoriamente o {@code false} si no se ha podido. */ public boolean sendMessage (MessageCommunication sender, String group, Message msg) { Pair <ArrayList <Pair <MessageCommunication, Message>>, ArrayList <MessageCommunication>> g = m_groups.get(group); if (g != null) { g.first.add(new Pair <>(sender, msg)); return true; } return false; } /** * Crea un nuevo grupo y devuelve su nombre. * * @return Nombre del grupo recién creado. */ public String createGroup () { String name = ""; do { name = Long.toUnsignedString(((Double) (Math.random() * Long.MAX_VALUE)).longValue()); } while (m_groups.containsKey(name)); ArrayList <Pair <MessageCommunication, Message>> msg_queue = new ArrayList <>(); ArrayList <MessageCommunication> group = new ArrayList <>(); m_groups.put(name, new Pair <>(msg_queue, group)); return name; } /** * Indica si un grupo ya ha sido creado. * * @param group * Grupo del cual se quiere comprobar su existencia. * @return Si el grupo existe o no. */ public boolean groupCreated (String group) { return m_groups.containsKey(group); } /** * Comprueba si un agente está suscrito a un grupo de mensajes determinado. * * @param agent * Agente que se quiere comprobar. * @param group * Grupo del que se quiere saber si el agente está suscrito. * @return Si el agente está suscrito al grupo o no. */ public boolean isSubscribed (MessageCommunication agent, String group) { Pair <ArrayList <Pair <MessageCommunication, Message>>, ArrayList <MessageCommunication>> g = m_groups.get(group); return g != null && g.second.contains(group); } /** * Envía todos los mensajes en las colas a sus destinatarios, pero evitando * enviar varias veces el mismo mensaje al mismo destinatario (es posible que * un agente envíe el mismo mensaje a varios grupos y que haya algún agente en * varios de ellos). */ public void flushMessageQueues () { // Creamos una cola de mensajes a ser enviados a cada agente, para detectar // las repeticiones de mensajes HashMap <MessageCommunication, ArrayList <Pair <MessageCommunication, Message>>> msgs = new HashMap <>(); // Iteramos sobre los grupos for (Entry <String, Pair <ArrayList <Pair <MessageCommunication, Message>>, ArrayList <MessageCommunication>>> entry: m_groups .entrySet()) { // Sólo nos interesan la cola de mensajes y de suscritos al grupo, no el // nombre del mismo Pair <ArrayList <Pair <MessageCommunication, Message>>, ArrayList <MessageCommunication>> e_val = entry.getValue(); // Recorremos todos los mensajes del grupo y los añadimos a la cola de // envío de todos los agentes del grupo for (Pair <MessageCommunication, Message> msg: e_val.first) for (MessageCommunication recv: e_val.second) addMsgToReceiverQueue(msgs, recv, msg); // Vaciamos la lista de mensajes, porque ya están a la cola para ser // procesados e_val.first.clear(); } // Recorremos las listas de mensajes de cada receptor para enviarle sus // mensajes pendientes for (Entry <MessageCommunication, ArrayList <Pair <MessageCommunication, Message>>> e: msgs .entrySet()) { MessageCommunication recv = e.getKey(); for (Pair <MessageCommunication, Message> msg: e.getValue()) recv.receiveMessage(msg.first, msg.second); } } /** * Añade un mensaje a la cola de envío de un agente, evitando añadir mensajes * que ya se encuentren en dicha cola. * * @param msgs * Estructura de datos con la lista de mensajes para cada receptor. * @param recv * Receptor del mensaje. * @param msg * Mensaje + Emisor del mensaje que se quiere añadir a la cola. */ private void addMsgToReceiverQueue ( Map <MessageCommunication, ArrayList <Pair <MessageCommunication, Message>>> msgs, MessageCommunication recv, Pair <MessageCommunication, Message> msg) { ArrayList <Pair <MessageCommunication, Message>> r_msgs = msgs.get(recv); if (r_msgs == null) { r_msgs = new ArrayList <Pair <MessageCommunication, Message>>(); msgs.put(recv, r_msgs); } for (Pair <MessageCommunication, Message> r_msg: r_msgs) { // Comparamos las referencias, porque no queremos desechar mensajes // iguales, sino repeticiones del mismo mensaje if (r_msg.first == msg.first && r_msg.second == msg.second) return; } // Si el mensaje es nuevo, lo añadimos a la lista de mensajes del receptor r_msgs.add(msg); } }