package org.openiot.cupus.mobile.entity.mobilebroker;
import android.content.Context;
import android.os.AsyncTask;
import org.openiot.cupus.artefact.Announcement;
import org.openiot.cupus.artefact.Publication;
import org.openiot.cupus.artefact.Subscription;
import org.openiot.cupus.artefact.TripletAnnouncement;
import org.openiot.cupus.artefact.TripletSubscription;
import org.openiot.cupus.message.Message;
import java.io.File;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* Created by Marko on 6.6.2014..
*/
public abstract class MobileBrokerOutgoingTCP extends AbstractMobileBroker {
/**
* Socket for sending messages (subscriptions and connect/disconnect/etc.)
* to the Broker
*/
protected Socket sendingSocket = null;
protected ObjectOutputStream sendingOut = null;
protected AnnouncementListener announcementListener;
protected Set<String> distinctSubscriptionAttributes = new HashSet<String>();
public MobileBrokerOutgoingTCP(String myName, String myBrokerIP, int myBrokerPort, Context context){
super(myName,myBrokerIP,myBrokerPort,context);
}
public MobileBrokerOutgoingTCP(File configFile, Context context){
super(configFile,context);
}
/**
* Used for connecting mobile broker to broker
*/
@Override
public void connect() {
new EstablishConnection().execute();
}
/**
* For terminating the connection... closes to outSocket and sets everything
* to null
*/
@Override
public void terminateConnection() {
new TerminateConnection().execute();
}
@Override
public void setAnnouncementListener(AnnouncementListener announcementListener) {
this.announcementListener = announcementListener;
}
/**
* Used for sending messages to broker
*
* @param sendMsg message to be sent
*/
@Override
protected void sendMessage(Message sendMsg) {
new SendMessage(sendMsg).execute();
}
/**
* AsyncTask for establishing connection in both ways
*/
public class EstablishConnection extends AsyncTask {
@Override
protected Object doInBackground(Object[] objects) {
startOutgoingConnection();
startIncomingConnection();
processCollectedWhileDisconnected();
return null;
}
}
/**
* AsyncTask for terminating both way connections
*/
private class TerminateConnection extends AsyncTask {
@Override
protected Object doInBackground(Object[] objects) {
terminateOutgoingConnectionInBackground();
terminateIncomingConnectionInBackground();
MobileBrokerOutgoingTCP.this.connected = false;
return null;
}
}
/**
* AsyncTask for sending message to server
*/
private class SendMessage extends AsyncTask {
private Message message;
public SendMessage(Message sendMsg) {
this.message = sendMsg;
}
@Override
protected Object doInBackground(Object[] objects) {
sendMessageInBackGround(message);
return null;
}
}
/**
* For establishing connection from user to server
*/
private void startOutgoingConnection(){
if (connected) {
String message = "Connect request received while being connected. Ignored.";
log.writeToLog(message);
return;
}
try {
sendingSocket = new Socket(this.myBrokerIP, this.myBrokerPort);
this.myPort = sendingSocket.getLocalPort();
sendingOut = new ObjectOutputStream(sendingSocket.getOutputStream());
sendingOut.flush();
} catch (UnknownHostException ex) {
String message = "Connecting failed - Unknown Broker Host or Port: " + ex;
log.writeToLog(message);
try {
sendingSocket.close();
} catch (Exception e) {
}
sendingSocket = null;
sendingOut = null;
return;
} catch (IOException ex) {
String message = "Failed to open stream to the Broker: " + ex;
log.writeToLog(message);
try {
sendingSocket.close();
} catch (Exception e) {
}
sendingSocket = null;
sendingOut = null;
return;
}
}
/**
* For establishing back connection from server to user
*/
protected abstract void startIncomingConnection();
/**
* Processing collected data while been disconnected from server
*/
private void processCollectedWhileDisconnected(){
//processing subscriptions, publications and announcements that were added while disconnected...
Iterator<Subscription> iteratorSub = outboxSubs.iterator();
while (iteratorSub.hasNext()) {
subscribe(iteratorSub.next());
iteratorSub.remove();
}
Iterator<Publication> iteratorPublication = outboxPubs.iterator();
while (iteratorPublication.hasNext()) {
publish(iteratorPublication.next());
iteratorPublication.remove();
}
Iterator<Announcement> iteratorAnn = outboxAnnouncements.iterator();
while (iteratorAnn.hasNext()) {
announce(iteratorAnn.next());
iteratorAnn.remove();
}
}
/**
* Send msg to server
* @param sendMsg
*/
protected void sendMessageInBackGround(Message sendMsg) {
try {
sendingOut.writeObject(sendMsg);
sendingOut.flush();
} catch (Exception e1) {
log.error("Message " + sendMsg + " not sent. Disconnecting because of connection problems.");
e1.printStackTrace();
terminateConnection();
}
}
/**
* Terminate outgoing connection
*/
private void terminateOutgoingConnectionInBackground(){
try {
sendingSocket.shutdownInput();
sendingSocket.shutdownOutput();
sendingSocket.close();
//the receiving socket will be closed from the server side
} catch (Exception e) {
e.printStackTrace();
}
sendingSocket = null;
sendingOut = null;
}
/**
* Terminate incoming connection
*/
protected abstract void terminateIncomingConnectionInBackground();
/**
* Used for handling notifications about new publications
*
* @param publication New Publication
*/
public void notify(Publication publication, boolean unpublish) {
synchronized (publicationListMutex) {
if (!unpublish) {
publicationList.add(publication);
} else {
publicationList.remove(publication);
}
}
if (!unpublish) {
log.writeToLog("Received a publication from broker (" + publication.getId() + ")");
notificationListener.notify(getId(), myName, publication);
} else {
log.writeToLog("Received an unpublication from broker (" + publication.getId() + ")");
}
}
/**
* Used for handling new subscriptions from the broker
*
* @param subscription New Subscription
*/
public void announcement(Subscription subscription, boolean unsubscribe) {
synchronized (subscriptionListMutex) {
if (!unsubscribe) {
TripletSubscription tripletSubscription = (TripletSubscription) subscription;
if (!distinctSubscriptionAttributes.containsAll(tripletSubscription.attributes())) {
distinctSubscriptionAttributes.addAll(tripletSubscription.attributes());
boolean sendSubscriptions = false;
for(Announcement announcement : activeAnnouncements) {
TripletAnnouncement tripletAnnouncement = (TripletAnnouncement) announcement;
if (tripletAnnouncement.coversSubscription(tripletSubscription)) {
sendSubscriptions = true;
}
}
if (sendSubscriptions) {
announcementListener.announcement(tripletSubscription.attributes(), unsubscribe);
}
}
brokerSubs.add(subscription);
log.writeToLog("Received subscription from broker (" + subscription + ")");
} else {
brokerSubs.remove(subscription);
TripletSubscription tripletSubscription = (TripletSubscription) subscription;
distinctSubscriptionAttributes.removeAll(tripletSubscription.attributes());
announcementListener.announcement(tripletSubscription.attributes(), unsubscribe);
log.writeToLog("Received unsubscription from broker (" + subscription.getId() + ")");
}
}
}
}