/**
* Copyright (c) 2011-2014, OpenIoT
*
* This file is part of OpenIoT.
*
* OpenIoT is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* OpenIoT 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 Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenIoT. If not, see <http://www.gnu.org/licenses/>.
*
* Contact: OpenIoT mailto: info@openiot.eu
*/
package org.openiot.cupus.entity.broker;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.openiot.cupus.artefact.ActiveAnnouncement;
import org.openiot.cupus.artefact.ActivePublication;
import org.openiot.cupus.artefact.ActiveSubscription;
import org.openiot.cupus.artefact.Announcement;
import org.openiot.cupus.artefact.HashtablePublication;
import org.openiot.cupus.artefact.Publication;
import org.openiot.cupus.artefact.Subscription;
import org.openiot.cupus.artefact.TopKWSubscription;
import org.openiot.cupus.artefact.TripletAnnouncement;
import org.openiot.cupus.artefact.TripletSubscription;
import org.openiot.cupus.common.UniqueObject;
import org.openiot.cupus.entity.NetworkEntity;
import org.openiot.cupus.message.InternalMessage;
import org.openiot.cupus.message.Message;
import org.openiot.cupus.message.external.AnnounceMessage;
import org.openiot.cupus.message.external.MobileBrokerDisconnectMessage;
import org.openiot.cupus.message.external.MobileBrokerRegisterGCMMessage;
import org.openiot.cupus.message.external.MobileBrokerRegisterMessage;
import org.openiot.cupus.message.external.PublishMessage;
import org.openiot.cupus.message.external.PublisherRegisterMessage;
import org.openiot.cupus.message.external.SubscribeMessage;
import org.openiot.cupus.message.external.SubscriberDisconnectMessage;
import org.openiot.cupus.message.external.SubscriberRegisterMessage;
import org.openiot.cupus.message.external.SubscriberUnregisterMessage;
import org.openiot.cupus.message.internal.ErrorMessage;
import org.openiot.cupus.message.internal.InfoMessage;
import org.openiot.cupus.message.internal.InitialAnnouncementMatchesMessage;
import org.openiot.cupus.message.internal.InitialMatchesMessage;
import org.openiot.cupus.message.internal.StartComponentMessage;
import org.openiot.cupus.topkw.SASubscription;
import org.openiot.cupus.util.LogWriter;
/**
* MessageReceiver is a component of a cloud-broker whose job is to receive all
* messages and requests from publishers and subscribers. It maintains a list of
* all regisered subscribers and publishers and has a thread for each of them
* that manages the communication with it and delegates the received requests to
* other broker components (via internal connection to CloudBroker superprocess)
* for processing.
*
* @author Eugen Rozic, Aleksandar Antonic
*
*/
public class MessageReceiver extends NetworkEntity {
private InternalCommunicationsThread intercomm = null;
private volatile Map<UUID, PublisherForBroker> registeredPublishers = new HashMap<UUID, PublisherForBroker>();
private volatile Map<UUID, SubscriberForBroker> registeredSubscribers = new HashMap<UUID, SubscriberForBroker>();
private volatile Map<UUID, MobileBrokerForBroker> registeredMobileBrokers = new HashMap<UUID, MobileBrokerForBroker>();
private volatile Set<ActivePublication> activePublications = new HashSet<ActivePublication>();
private volatile Set<ActiveAnnouncement> activeAnnouncements = new HashSet<ActiveAnnouncement>();
private Object mutexPublisherList = new Object();
private Object mutexSubscriberList = new Object();
private Object mutexMobileBroekrList = new Object();
private Object mutexActivePublicationsList = new Object();
private Object mutexActiveAnnouncementsList = new Object();
protected boolean isRunning = false;
private boolean testing = false;
@SuppressWarnings("unused")
private boolean logWriting = false;
private LogWriter log = null;
/**
* Constructs the message receiver and starts it's internal communication
* thread.
*/
private MessageReceiver(String brokerName, String brokerIP, int brokerPort,
boolean testing, boolean logWriting,
ObjectInputStream in, ObjectOutputStream out) {
super(brokerName, brokerIP, brokerPort);
this.testing = testing;
this.logWriting = logWriting;
this.log = new LogWriter(brokerName + "_messageReceiver.log", logWriting, false);
this.intercomm = new InternalCommunicationsThread(in, out);
new Thread(intercomm).start();
}
/**
* Starts a new BrokerListenerThread to accept incoming connections and
* spawn new BrokerServingThreads to process each of the connections.
*/
private void start() {
new Thread(new BrokerListenerThread()).start();
this.isRunning = true;
informBroker("MessageReceiver started!", false);
}
/**
* Kills the (sub)process (which will automatically close all the queue
* connections and close the stream to the CloudBroker whose input stream
* will in turn throw an EOFException that will start the shutdown of the
* CloudBroker and all it's children processes.
*/
private void shutdown() {
isRunning = false; //not really necessary
System.exit(-1);
}
/**
* Removes the publisher from the registeredPublishers map and informs the
* CloudBroker.
*/
protected void removePublisher(UUID publisherID) {
PublisherForBroker pub;
synchronized (mutexPublisherList) {
pub = registeredPublishers.remove(publisherID);
}
//informBroker("Publisher " + pub + " removed (disconnected) from broker.", false);
}
/**
* Removes the publisher from the registeredPublishers map and informs the
* CloudBroker.
*/
protected void removeSubscriber(SubscriberUnregisterMessage msg) {
synchronized (mutexSubscriberList) {
registeredSubscribers.remove(msg.getEntityID());
}
sendInternalMessage(msg);
}
/**
* Removes the mobile broker from the registeredMobileBroker map and informs
* the CloudBroker.
*/
protected void removeMobileBroker(MobileBrokerDisconnectMessage msg) {
synchronized (mutexMobileBroekrList) {
registeredMobileBrokers.remove(msg.getEntityID());
}
synchronized (mutexSubscriberList) {
registeredSubscribers.remove(msg.getEntityID());
}
synchronized (mutexPublisherList) {
registeredPublishers.remove(msg.getEntityID());
}
sendInternalMessage(msg);
}
/**
* Checks if the carried publication is valid and passes the Attributes
* check. If it does it is processed and forwarded to the CloudBroker.
*
* @param pubID ID of entity that publishes
* @param msg The received PublishMessage carrying the publication
*/
protected void publish(UUID pubID, PublishMessage msg) {
PublisherForBroker publisher = registeredPublishers.get(pubID);
Publication publication = msg.getPublication();
if (publication == null || !publication.isValid()) {
return;
}
if (publication instanceof HashtablePublication) {
if (msg.isUnpublish()) {
unpublishBoolean(msg, publisher);
} else {
publishBoolean(msg, publisher);
}
} else {
informBroker("Unknown publication type received! (" + publication.getClass().getName() + ")", true);
return;
}
}
/**
* Adds the (hashtable) publication to the set of active publications and
* forwards the PublishMessage to the inside of the broker for further
* processing.
*/
private void publishBoolean(PublishMessage msg, PublisherForBroker publisher) {
Publication publication = msg.getPublication();
ActivePublication pubPair = new ActivePublication(publisher.pubID, publication);
msg.setPublication(pubPair);
sendInternalMessage(msg); //forward to broker for processing...
synchronized (this.mutexActivePublicationsList) {
if (!activePublications.add(pubPair)) {
informBroker("Publication " + publication + " from publisher " + publisher + " already present on broker!", false);
return; //do nothing if the broker already had this publication...
}
}
}
/**
* Removes the (hashtable) publication from the set of active publications
* and forwards the PublishMessage to the inside of the broker for further
* processing.
*/
private void unpublishBoolean(PublishMessage msg, PublisherForBroker publisher) {
Publication publication = msg.getPublication();
ActivePublication pubPair = new ActivePublication(publisher.pubID, publication);
msg.setPublication(pubPair);
sendInternalMessage(msg); //forward to broker for processing...
synchronized (this.mutexActivePublicationsList) {
if (!this.activePublications.remove(pubPair)) {
informBroker("(unpublish) Publication " + publication + " from publisher " + publisher + " not found on broker!", true);
return;
}
}
}
/**
* Checks if the carried subscription is valid and passes the Attributes
* check. If it does it is processed and forwarded to the CloudBroker.
*
* @param subID ID of entity that subscribes
* @param msg The received SubscribeMessage carrying the subscription
*/
protected void subscribe(UUID subID, SubscribeMessage msg) {
SubscriberForBroker subscriber = registeredSubscribers.get(subID);
Subscription subscription = msg.getSubscription();
if (subscription == null || !subscription.isValid()) {
return;
}
if (subscription instanceof TripletSubscription) {
if (msg.isUnsubscribe()) {
unsubscribeBoolean(msg, subscriber);
} else {
subscribeBoolean(msg, subscriber);
}
} else if (subscription instanceof TopKWSubscription) {
if (msg.isUnsubscribe()) {
unsubscribeTopKW(msg, subscriber);
} else {
subscribeTopKW(msg, subscriber);
}
} else {
informBroker("Unknown subscription type received! (" + subscription.getClass().getName() + ")", true);
return;
}
}
/**
* Forwards the msg to the CloudBroker for further processing... (replacing
* the Subscription in it with an ActiveSubscription beforehand).<br>
* After forwarding the msg it checks which of the activePublications are
* covered by the new subscription (removing the expired ones in the
* process) and sends all of them that do to the DeliveryService to be
* delivered to the subscriber.
*/
private void subscribeBoolean(SubscribeMessage msg, SubscriberForBroker subscriber) {
Subscription subscription = msg.getSubscription();
ActiveSubscription subPair = new ActiveSubscription(subscriber.subID, subscription);
msg.setSubscription(subPair);
sendInternalMessage(msg); //forward to broker for processing...
//removing expired publications from the activePublications set...
List<Publication> toDeliver = new ArrayList<Publication>();
synchronized (this.mutexActivePublicationsList) { //deliver active publications that fit to the new subscription to its subscriber
Iterator<ActivePublication> actPubIter = activePublications.iterator();
while (actPubIter.hasNext()) {
ActivePublication pub = actPubIter.next();
if (pub.isValid()) {
if (subscription.coversPublication(pub.getPublication())) {
toDeliver.add(pub.getPublication());
}
} else {
actPubIter.remove();
}
}
}
sendInternalMessage(new InitialMatchesMessage(subscriber.subID, toDeliver));
//removing expired announcements from the activeAnnouncement set...
Set<UUID> mobileBrokerstoDeliver = new HashSet<UUID>();
synchronized (this.mutexActiveAnnouncementsList) { //deliver subscription that fit to the active announcement to its mobile broker
Iterator<ActiveAnnouncement> actAnnIter = activeAnnouncements.iterator();
while (actAnnIter.hasNext()) {
ActiveAnnouncement ann = actAnnIter.next();
if (ann.isValid()) {
if (ann.coversSubscription(subscription)) {
mobileBrokerstoDeliver.add(ann.getMobileBrokerID());
}
} else {
actAnnIter.remove();
}
}
}
sendInternalMessage(new InitialAnnouncementMatchesMessage(msg, mobileBrokerstoDeliver));
}
/**
* Forwards the msg to the CloudBroker for further processing... (replacing
* the Subscription in it with an ActiveSubscription beforehand)
*/
private void unsubscribeBoolean(SubscribeMessage msg, SubscriberForBroker subscriber) {
ActiveSubscription subPair = new ActiveSubscription(subscriber.subID, msg.getSubscription());
msg.setSubscription(subPair);
sendInternalMessage(msg); //forward to broker for processing...
Subscription subscription = msg.getSubscription();
//removing expired announcements from the activeAnnouncement set...
Set<UUID> mobileBrokerstoDeliver = new HashSet<UUID>();
synchronized (this.mutexActiveAnnouncementsList) { //deliver subscription that fit to the active announcement to its mobile broker
Iterator<ActiveAnnouncement> actAnnIter = activeAnnouncements.iterator();
while (actAnnIter.hasNext()) {
ActiveAnnouncement ann = actAnnIter.next();
if (ann.isValid()) {
if (ann.coversSubscription(subscription)) {
mobileBrokerstoDeliver.add(ann.getMobileBrokerID());
}
} else {
actAnnIter.remove();
}
}
}
sendInternalMessage(new InitialAnnouncementMatchesMessage(msg, mobileBrokerstoDeliver));
}
private void subscribeTopKW(SubscribeMessage msg, SubscriberForBroker subscriber) {
if (msg.getSubscription() instanceof TopKWSubscription) {
TopKWSubscription subscription = (TopKWSubscription) msg.getSubscription();
SASubscription subPair = new SASubscription(subscriber.subID, (TopKWSubscription) subscription);
msg.setSubscription(subPair);
sendInternalMessage(msg); //forward to broker for processing...
//removing expired announcements from the activeAnnouncement set...
Set<UUID> mobileBrokerstoDeliver = new HashSet<UUID>();
synchronized (this.mutexActiveAnnouncementsList) { //deliver subscription that fit to the active announcement to its mobile broker
Iterator<ActiveAnnouncement> actAnnIter = activeAnnouncements.iterator();
while (actAnnIter.hasNext()) {
ActiveAnnouncement ann = actAnnIter.next();
if (ann.isValid()) {
if (ann.coversSubscription(subscription)) {
mobileBrokerstoDeliver.add(ann.getMobileBrokerID());
}
} else {
actAnnIter.remove();
}
}
}
sendInternalMessage(new InitialAnnouncementMatchesMessage(msg, mobileBrokerstoDeliver));
}
}
private void unsubscribeTopKW(SubscribeMessage msg, SubscriberForBroker subscriber) {
SASubscription subPair = new SASubscription(subscriber.getSubID(), (TopKWSubscription) msg.getSubscription());
msg.setSubscription(subPair);
sendInternalMessage(msg); //forward to broker for processing...
}
/**
* Checks if the carried announcement is valid and passes the Attributes
* check. If it does it is processed and forwarded to the CloudBroker.
*
* @param mbID ID of mobile broker entity
* @param msg The received SubscribeMessage carrying the subscription
*/
protected void announce(UUID mbID, AnnounceMessage msg) {
MobileBrokerForBroker mobileBroker = registeredMobileBrokers.get(mbID);
Announcement announcement = msg.getAnnouncement();
if (announcement == null || !announcement.isValid()) {
return;
}
if (announcement instanceof TripletAnnouncement) {
if (msg.isRevokeAnnouncement()) {
revokeAnnouncementBoolean(msg, mobileBroker);
} else {
announceBoolean(msg, mobileBroker);
}
} else {
informBroker("Unknown announcement type received! (" + announcement.getClass().getName() + ")", true);
return;
}
}
/**
* Forwards the msg to the CloudBroker for further processing... (replacing
* the Announcement in it with an ActiveAnnouncement beforehand).<br>
*/
private void announceBoolean(AnnounceMessage msg, MobileBrokerForBroker mobileBroker) {
Announcement announcement = msg.getAnnouncement();
ActiveAnnouncement annPair = new ActiveAnnouncement(mobileBroker.mbID, announcement);
msg.setAnnouncement(annPair);
sendInternalMessage(msg); //forward to broker for processing...
synchronized (this.mutexActiveAnnouncementsList) {
if (!activeAnnouncements.add(annPair)) {
informBroker("Announcement " + announcement + " from mobile broker " + mobileBroker + " already present on broker!", false);
return; //do nothing if the broker already had this publication...
}
}
}
/**
* Forwards the msg to the CloudBroker for further processing... (replacing
* the Announcement in it with an ActiveSubscription beforehand)
*/
private void revokeAnnouncementBoolean(AnnounceMessage msg, MobileBrokerForBroker mobileBroker) {
ActiveAnnouncement annPair = new ActiveAnnouncement(mobileBroker.mbID, msg.getAnnouncement());
msg.setAnnouncement(annPair);
sendInternalMessage(msg); //forward to broker for processing...
synchronized (this.mutexActiveAnnouncementsList) {
if (!this.activeAnnouncements.remove(annPair)) {
informBroker("Revoke announcement " + annPair.getAnnouncement() + " from mobile broker " + mobileBroker + " not found on broker!", true);
return;
}
}
}
/**
* Convinience method that sends an ErrorMessage or an InfoMessage to the
* CloudBroker, depending if the reporting flag is set or not. It also logs
* the message to this MessageReceiver's log file (if logging is on).
*/
protected void informBroker(String msg, boolean error) {
if (error) {
log.writeToLog("ERROR: " + msg);
if (testing) {
sendInternalMessage(new ErrorMessage(msg));
}
} else {
log.writeToLog(msg);
if (testing) {
sendInternalMessage(new InfoMessage(msg));
}
}
}
/**
* Convinience method to send a message to the CloudBroker. It has to be
* synchronized because multiple threads may want to send something at the
* same time.
*/
synchronized protected void sendInternalMessage(Object msg) {
try {
intercomm.out.writeObject(msg);
intercomm.out.flush();
} catch (IOException e) {
//if component found terminated shut everything down...
this.shutdown();
}
}
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
/**
* For communicating with the starting process (CloudBroker)
*/
private class InternalCommunicationsThread implements Runnable {
ObjectInputStream in = null;
ObjectOutputStream out = null;
/**
* Sets the stream and reads from in and sets the singleton class.
*
*/
public InternalCommunicationsThread(ObjectInputStream in, ObjectOutputStream out) {
this.in = in;
this.out = out;
}
@Override
public void run() {
while (true) {
Object objIn = null;
try {
objIn = in.readObject();
} catch (Exception e) {
MessageReceiver.this.shutdown();
}
if (objIn instanceof InternalMessage) {
if (objIn instanceof StartComponentMessage) {
//MessageReceiver.this.start();
} else {
informBroker("MessageReceiver: Unexpected internal message received from CloudBroker - " + objIn.getClass().getName(), true);
}
} else if (objIn instanceof Message) {
if (objIn instanceof SubscriberDisconnectMessage) {
UUID subID = ((SubscriberDisconnectMessage) objIn).getEntityID();
SubscriberForBroker sub = registeredSubscribers.get(subID);
sub.terminateConnection();
} if (objIn instanceof MobileBrokerDisconnectMessage) {
UUID mbID = ((MobileBrokerDisconnectMessage) objIn).getEntityID();
MobileBrokerForBroker mb = registeredMobileBrokers.get(mbID);
if (mb != null) {
mb.terminateConnection();
}
SubscriberForBroker sub = registeredSubscribers.get(mbID);
if (sub != null) {
sub.terminateConnection();
}
PublisherForBroker pub = registeredPublishers.get(mbID);
if (pub != null) {
pub.terminateConnection();
}
} else {
informBroker("MessageReceiver: Unexpected external message received from CloudBroker - " + objIn.getClass().getName(), true);
}
} else {
String errMsg = "MessageReceiver: Received message is not of class Message"
+ " or InternalMessage! (" + objIn.getClass() + " instead). Ignoring...";
informBroker(errMsg, true);
}
}
}
}
/**
* This class is used for accepting requests from new users and creating
* serving threads that will serve them.
*/
private class BrokerListenerThread implements Runnable {
private MessageReceiver broker = MessageReceiver.this;
private ServerSocket serverSocket;
/**
* Primary run method that upon request creates new serving thread
*/
public void run() {
boolean invalidPort = true;
int tries = 0;
while (invalidPort) { //crating new server socket (listener)
try {
serverSocket = new ServerSocket(broker.myPort, 100);
serverSocket.setReuseAddress(true);
invalidPort = false;
} catch (IOException e) {
broker.myPort++;
tries++;
if (tries >= 10) {
String errMsg = "Port(s) in use. Stopping trying to start the broker.";
sendInternalMessage(new ErrorMessage(errMsg));
MessageReceiver.this.shutdown();
}
}
}
sendInternalMessage(new InfoMessage("Listener Created on port " + serverSocket.getLocalPort()));
while (broker.isRunning) {
Socket s = null;
try {
s = serverSocket.accept();
new Thread(new BrokerServingThread(s)).start();
} catch (SecurityException e) {
String errMsg = "Not allowed to accept a connection from: " + s.getInetAddress().getHostAddress() + ":" + s.getPort();
informBroker(errMsg, true);
} catch (Exception e) {
String errMsg = "Unmanagable ServerSocket exception occured. Shutting the broker down...";
sendInternalMessage(new ErrorMessage(errMsg));
MessageReceiver.this.shutdown();
}
}
}
}
/**
* This class is used for handling the connection initiation
*/
private class BrokerServingThread implements Runnable {
private MessageReceiver broker = MessageReceiver.this;
private Socket socket;
private Message message;
ObjectInputStream inFromClient;
ObjectOutputStream outToClient;
/**
* @param socket Socket for communicating with client (entity -
* publisher/subscriber)
*/
public BrokerServingThread(Socket socket) {
this.socket = socket;
}
/**
* Primary thread method, it waits for user input (via message),
* recognizes what type message is and calls appropriate method.
*/
@Override
public void run() {
if (!broker.isRunning) {
return;
}
try {
outToClient = new ObjectOutputStream(socket.getOutputStream());
outToClient.flush();
inFromClient = new ObjectInputStream(socket.getInputStream());
} catch (Exception e1) {
try {
socket.close();
} catch (Exception e2) {
}
return;
}
Object objIn = null;
try {
objIn = inFromClient.readObject();
} catch (Exception e1) {
try {
socket.close();
} catch (Exception e2) {
}
return;
}
if (!(objIn instanceof Message)) {
try {
socket.close();
} catch (Exception e2) {
}
return;
} else {
message = (Message) objIn;
}
if (message instanceof SubscriberRegisterMessage) {
registerSubscriber((SubscriberRegisterMessage) message);
} else if (message instanceof PublisherRegisterMessage) {
connectPublisher((PublisherRegisterMessage) message);
} else if (message instanceof MobileBrokerRegisterMessage) {
connectMobileBroker((MobileBrokerRegisterMessage) message);
} else if (message instanceof MobileBrokerRegisterGCMMessage) {
connectMobileBroker((MobileBrokerRegisterGCMMessage) message);
} else {
//TODO send some sort of NACK...
}
}
/**
* Used for registering a new subscriber
*/
private void registerSubscriber(SubscriberRegisterMessage msg) {
SubscriberForBroker subscriber = broker.registeredSubscribers.get(msg.getEntityID());
if (subscriber != null) {
informBroker("Reconnecting a previously registered subscriber " + subscriber + ".", false);
if (subscriber.setSocketAndStreams(socket, inFromClient, outToClient)) {
new Thread(subscriber).start(); //start communication thread with subscriber
} else {
//this should never happen...
informBroker("Subscriber " + subscriber + " was already connected?!", false);
}
} else {
subscriber = new SubscriberForBroker(msg.getEntityName(),
socket.getInetAddress().getHostAddress(), socket.getPort(),
msg.getEntityID(), broker);
synchronized (mutexSubscriberList) {
broker.registeredSubscribers.put(msg.getEntityID(), subscriber);
}
subscriber.setSocketAndStreams(socket, inFromClient, outToClient);
new Thread(subscriber).start();
sendInternalMessage(new ErrorMessage("RECEIVER!!!1"));
informBroker("Subscriber " + subscriber + " registered.", false);
}
//no need for ACK sending - establishing the return connection
//from deliveryService to subscriber will be like ACK
sendInternalMessage(msg); //forward to DeliveryService for it to setup the queue and connection beck to subscriber etc.
}
/**
* Used for registering a new mobile broker
*/
private void connectMobileBroker(MobileBrokerRegisterMessage msg) {
MobileBrokerForBroker mobileBroker = broker.registeredMobileBrokers.get(msg.getEntityID());
if (mobileBroker != null) {
informBroker("Reconnecting a previously registered mobile broker " + mobileBroker + ".", false);
if (mobileBroker.setSocketAndStreams(socket, inFromClient, outToClient)) {
new Thread(mobileBroker).start(); //start communication thread with mobile broker
} else {
//this should never happen...
informBroker("Mobile broker " + mobileBroker + " was already connected?!", false);
}
} else {
mobileBroker = new MobileBrokerForBroker(msg.getEntityName(),
socket.getInetAddress().getHostAddress(), socket.getPort(),
msg.getEntityID(), broker);
synchronized (mutexMobileBroekrList) {
broker.registeredMobileBrokers.put(msg.getEntityID(), mobileBroker);
}
mobileBroker.setSocketAndStreams(socket, inFromClient, outToClient);
SubscriberForBroker subscriber = new SubscriberForBroker(msg.getEntityName(),
socket.getInetAddress().getHostAddress(), socket.getPort(),
msg.getEntityID(), broker);
synchronized (mutexSubscriberList) {
broker.registeredSubscribers.put(msg.getEntityID(), subscriber);
}
subscriber.setSocketAndStreams(socket, inFromClient, outToClient);
PublisherForBroker publisher = new PublisherForBroker(msg.getEntityName(),
socket.getInetAddress().getHostAddress(), socket.getPort(),
msg.getEntityID(), broker);
synchronized (mutexPublisherList) {
broker.registeredPublishers.put(msg.getEntityID(), publisher);
}
publisher.setSocketAndStreams(socket, inFromClient, outToClient);
new Thread(mobileBroker).start();
informBroker("Mobile Broker " + mobileBroker + " registered.", false);
}
//no need for ACK sending - establishing the return connection
//from deliveryService to subscriber will be like ACK
sendInternalMessage(msg); //forward to DeliveryService for it to setup the queue and connection beck to mobile broker etc.
}
/**
* Used for registering a new mobile broker
*/
private void connectMobileBroker(MobileBrokerRegisterGCMMessage msg) {
MobileBrokerForBroker mobileBroker = broker.registeredMobileBrokers.get(msg.getEntityID());
if (mobileBroker != null) {
informBroker("Reconnecting a previously registered mobile broker " + mobileBroker + ".", false);
if (mobileBroker.setSocketAndStreams(socket, inFromClient, outToClient)) {
new Thread(mobileBroker).start(); //start communication thread with mobile broker
} else {
//this should never happen...
informBroker("Mobile broker " + mobileBroker + " was already connected?!", false);
}
} else {
mobileBroker = new MobileBrokerForBroker(msg.getEntityName(),
socket.getInetAddress().getHostAddress(), socket.getPort(),
msg.getEntityID(), broker);
synchronized (mutexMobileBroekrList) {
broker.registeredMobileBrokers.put(msg.getEntityID(), mobileBroker);
}
mobileBroker.setSocketAndStreams(socket, inFromClient, outToClient);
SubscriberForBroker subscriber = new SubscriberForBroker(msg.getEntityName(),
socket.getInetAddress().getHostAddress(), socket.getPort(),
msg.getEntityID(), broker);
synchronized (mutexSubscriberList) {
broker.registeredSubscribers.put(msg.getEntityID(), subscriber);
}
subscriber.setSocketAndStreams(socket, inFromClient, outToClient);
PublisherForBroker publisher = new PublisherForBroker(msg.getEntityName(),
socket.getInetAddress().getHostAddress(), socket.getPort(),
msg.getEntityID(), broker);
synchronized (mutexPublisherList) {
broker.registeredPublishers.put(msg.getEntityID(), publisher);
}
publisher.setSocketAndStreams(socket, inFromClient, outToClient);
new Thread(mobileBroker).start();
informBroker("Mobile Broker " + mobileBroker + " registered.", false);
}
//no need for ACK sending - establishing the return connection
//from deliveryService to subscriber will be like ACK
sendInternalMessage(msg); //forward to DeliveryService for it to setup the queue and connection beck to mobile broker etc.
}
/**
* Used for registering a new publisher (with publishers there is no
* difference between registering and connecting)
*/
private void connectPublisher(PublisherRegisterMessage msg) {
informBroker("Received a publisher register message...", false);
PublisherForBroker publisher = broker.registeredPublishers.get(msg.getEntityID());
if (publisher != null) { //should not happen
if (publisher.isRunning()) {
informBroker("Publisher " + publisher + " already connected?!", true);
return;
} else {
informBroker("WARNING: Publisher " + publisher + " was found registered on broker but unconnected?!", true);
publisher.setSocketAndStreams(socket, inFromClient, outToClient);
new Thread(publisher).start();
}
} else {
publisher = new PublisherForBroker(msg.getEntityName(),
socket.getInetAddress().getHostAddress(), socket.getPort(),
msg.getEntityID(), broker);
synchronized (mutexPublisherList) {
broker.registeredPublishers.put(msg.getEntityID(), publisher);
}
publisher.setSocketAndStreams(socket, inFromClient, outToClient);
new Thread(publisher).start();
informBroker("Publisher " + publisher + " connected.", false);
}
//TODO send some osrt of ACK
}
}
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
/**
* Used for starting of the component. It creates an instance with the
* received arguments and starts a thread that listens on the input stream.
*/
public static void main(String[] args) {
ObjectInputStream in = null;
ObjectOutputStream out = null;
try {
out = new ObjectOutputStream(System.out);
out.flush();
in = new ObjectInputStream(System.in);
} catch (Exception e) {
//don't write anything, just kill the (sub)process.
System.exit(-1);
}
String brokerName = null;
String brokerIP = null;
int brokerPort = -1;
boolean testing = false;
boolean logWriting = false;
try {
brokerName = args[0];
brokerIP = args[1];
if (!brokerIP.equals(UniqueObject.getLocalIP())) {
throw new Exception("IP's don't match!");
}
brokerPort = Integer.parseInt(args[2]);
if (brokerPort <= 1024 || brokerPort > 49151) {
throw new NumberFormatException("Given port number is <1024 or >49151!");
}
testing = Boolean.parseBoolean(args[3]);
logWriting = Boolean.parseBoolean(args[4]);
} catch (IndexOutOfBoundsException e) {
String errMsg = e.getMessage() + " Not enough argument sent when starting DeliveryService! (5 needed)";
sendObject(new ErrorMessage(errMsg), out);
System.exit(-1);
} catch (NumberFormatException e) {
String errMsg = e.getMessage();
sendObject(new ErrorMessage(errMsg), out);
System.exit(-1);
} catch (Exception e) {
sendObject(new ErrorMessage(e.getMessage()), out);
System.exit(-1);
}
//create a new MessageReceiver, a thread listening on System.in is
//automatically started and keeps the process alive
MessageReceiver mr = new MessageReceiver(brokerName, brokerIP, brokerPort, testing, logWriting, in, out);
mr.start();
sendObject(new InfoMessage("MessageReceiver created!"), out);
}
/**
* convinience method for sending object and terminating process if not
* successfull.
*/
private static void sendObject(Object o, ObjectOutputStream out) {
try {
out.writeObject(o);
out.flush();
} catch (Exception e) {
System.exit(-1);
}
}
}