package network.platform.ip;
import model.interfaces.network.INetworkBroadcastSender;
import model.interfaces.network.INetworkTraffic;
import network.platform.NetworkPlatformMessage;
import util.Parameters;
import java.util.Vector;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.InetAddress;
import java.net.PortUnreachableException;
import java.io.IOException;
import util.NetworkAddress;
import platform.servicesregister.ServicesRegisterManager;
import platform.context.ContextManager;
import platform.context.ContextInformation;
/**
* Send NetworkMessages on UDP multicast for the platform
*
* @author Dalmau
*/
public class IPPlatformBroadcastMessagesSender extends Thread implements INetworkBroadcastSender, INetworkTraffic {
private Vector<NetworkPlatformMessage> messagesEnAttente; // messages du superviseur
private boolean enMarche, arrete, termine;
private NetworkAddress monAdresse;
private ContextManager gestionnaireContexte;
private int sendedData;
/**
* Creates the multicast sender
* @param adr the address of the fost on this network
*/
public IPPlatformBroadcastMessagesSender(NetworkAddress adr) {
monAdresse = adr;
messagesEnAttente = new Vector<NetworkPlatformMessage>();
enMarche = true;
arrete = false;
sendedData = 0;
gestionnaireContexte = (ContextManager)ServicesRegisterManager.platformWaitForService(Parameters.CONTEXT_MANAGER);
setPriority(Thread.NORM_PRIORITY+2);
start();
}
/**
* Sends a message on UDP multicast. This message is put in a queue and sent by the sender's thread.
* @param tr the message to send
*/
public synchronized void posterMessage(NetworkPlatformMessage tr) {
messagesEnAttente.addElement(tr.clone());
notifyAll(); // debloquer le thread d'envoi de messages
}
/**
* Sends a message on UDP multicast. This message is put in a queue and sent by the sender's thread.
* This message is completed by the address of the sending host on this network
* @param tr message to complete and to send
*/
public synchronized void posterMessageIncomplet(NetworkPlatformMessage tr) {
NetworkPlatformMessage env = tr.clone();
env.addContent(env.getContent()+monAdresse.getNormalizedAddress());
messagesEnAttente.addElement(env);
notifyAll(); // debloquer le thread d'envoi de messages
}
/**
* Stops the sender. The senders goes on running until there are no more messages to send, then stops
*/
public synchronized void stopThread() {
arrete = true;
notifyAll();
}
/**
* Returns the local host address on the network on which this sender works
* @return the local host address on the network on which this sender works
*/
public NetworkAddress getSenderAddress() {
return monAdresse;
}
/**
* Waits until there are no more messages to send
*/
public synchronized void waitForBufferEmpty() {
while (!termine) {
try { wait(); }
catch (InterruptedException ie) {}
}
}
// Methode semaphore de traitement des demandes d'emission de messages par la PF
private synchronized void traiterDemandes() {
while (messagesEnAttente.size() == 0) {
try {
if (arrete) {
enMarche = false;
return;
}
else wait(); // si pas de message se bloquer
}
catch (InterruptedException ie) { }
}
// on a un message a envoyer (deblocage du thread)
NetworkPlatformMessage envoi = messagesEnAttente.elementAt(0);
messagesEnAttente.removeElementAt(0); // enlever le message
if (envoi.getExpeditorAddress().equals("")) envoi.setExpeditorAddress(monAdresse.getNormalizedAddress());
int compteEssaiRestants = 3;
DatagramSocket socketReponse = null;
while (compteEssaiRestants != 0) {
try {
socketReponse = new DatagramSocket();
try {
byte[] buf = envoi.toByteArray();
sendedData = sendedData+buf.length;
if (buf.length>Parameters.MAX_BROADCAST_MESSAGE_SIZE) {
System.err.println("PF Broadcast sender: message size too long, message not sent");
compteEssaiRestants = 0;
}
else {
DatagramPacket packet;
packet = new DatagramPacket(buf, buf.length, InetAddress.getByName(Parameters.UDP_BROADCAST_GROUP_PF), Parameters.PORT_UDP_BROADCAST_PF);
socketReponse.send(packet);
socketReponse.close();
compteEssaiRestants = 0;
}
}
catch (PortUnreachableException ioe) {
if (socketReponse != null) socketReponse.close();
if (arrete) compteEssaiRestants =0;
else {
compteEssaiRestants--;
if (compteEssaiRestants == 0) {
gestionnaireContexte.signalEvent(new ContextInformation("Error when sending PF broadcast: port unreachable"));
}
}
}
catch (SecurityException e) {
compteEssaiRestants = 0;
if (!arrete) gestionnaireContexte.signalEvent(new ContextInformation("Error when sending PF broadcast: securitu"));
}
catch (SocketException e) {
if (socketReponse != null) socketReponse.close();
if (arrete) compteEssaiRestants =0;
else {
compteEssaiRestants--;
if (compteEssaiRestants == 0) {
gestionnaireContexte.signalEvent(new ContextInformation("Error when sending PF broadcast: socket"));
}
}
}
catch (IOException e) {
if (socketReponse != null) socketReponse.close();
if (arrete) compteEssaiRestants =0;
else {
compteEssaiRestants--;
if (compteEssaiRestants == 0) {
gestionnaireContexte.signalEvent(new ContextInformation("Error when sending PF broadcast: IO"));
}
}
}
}
catch (SocketException se) {
if (arrete) compteEssaiRestants =0;
else {
compteEssaiRestants--;
if (compteEssaiRestants == 0) {
gestionnaireContexte.signalEvent(new ContextInformation("Error creating platform message sender broadcast socket"));
}
}
}
}
}
/**
* Wait for messages to send and send them
*/
@Override
public void run() {
// boucle de traitement des messages a envoyer
termine = false;
while (enMarche) {
traiterDemandes();
}
terminer();
}
private synchronized void terminer() {
termine = true;
notifyAll();
}
/**
* Returns the number of bytes sent on network by the PF in broadcast since the last call of this method.
* @return the number of bytes sent on network by the PF in broadcast since the last call of this method.
*/
public int getDataSize() {
int ret = sendedData;
sendedData = 0;
return ret;
}
}