/** * 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.publisher; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Iterator; import java.util.Properties; import java.util.UUID; import org.openiot.cupus.artefact.HashtablePublication; import org.openiot.cupus.artefact.Publication; import org.openiot.cupus.entity.NetworkEntity; import org.openiot.cupus.entity.publisher.PublisherInterface; import org.openiot.cupus.message.Message; import org.openiot.cupus.message.external.PublishMessage; import org.openiot.cupus.message.external.PublisherDisconnectMessage; import org.openiot.cupus.message.external.PublisherRegisterMessage; import org.openiot.cupus.util.LogWriter; import org.openiot.cupus.util.ReadingWritingXML; /** * This is an implementation of a publisher that knows how to communicate with * the cloud-broker. * */ public class Publisher extends NetworkEntity implements PublisherInterface { //List of all published Publications private ArrayList<Publication> allPubs = new ArrayList<Publication>(); // List od all active Publications private ArrayList<Publication> activePubs = new ArrayList<Publication>(); private ArrayList<Publication> outboxPubs = new ArrayList<Publication>(); private String myBrokerIP; private int myBrokerPort; private boolean connected; private LogWriter log; private boolean logWriting = false; private boolean testing = true; private Socket socket; private ObjectOutputStream out; /** * Constructor - publisher can be created via configuration file or directly * * @param myName Publisher name * @param myBrokerIP Publisher's connecting broker IP address * @param myBrokerPort Publisher's connecting broker port */ public Publisher(String myName, String myBrokerIP, int myBrokerPort) { super(myName, getLocalIP(), -1); if (this.myIP.equals("")) { log.writeToLog("Does not have correct IP address " + this.myIP); this.myIP = "localhost"; } this.myBrokerIP = myBrokerIP; this.myBrokerPort = myBrokerPort; this.connected = false; log = new LogWriter(this.myName + "_publisherLog.txt", logWriting, testing); log.writeToLog("Publisher name: " + this.myName); log.writeToLog("Publisher broker port: " + this.myBrokerPort); log.writeToLog("Publisher broker IP: " + this.myBrokerIP); log.writeToLog(""); } /** * Constructor - subscriber can be created via configuration file or * directly */ public Publisher(File configFile) { super("", getLocalIP(), -1); if (this.myIP.equals("")) { log.writeToLog("Does not have correct IP address " + this.myIP, true); this.myIP = "localhost"; } try { Properties pubProps = new Properties(); FileInputStream fileIn = new FileInputStream(configFile); pubProps.load(fileIn); fileIn.close(); this.myName = pubProps.getProperty("publisherName"); if (this.myName==null) throw new NullPointerException("Name must be defined!"); this.myBrokerIP = pubProps.getProperty("brokerIP"); if (this.myBrokerIP==null) throw new NullPointerException("BrokerIP must be defined!"); this.myBrokerPort = Integer.parseInt(pubProps.getProperty("brokerPort")); if (pubProps.getProperty("testing", "false").toLowerCase().equals("false")){ this.testing = false; } else if (pubProps.getProperty("testing").toLowerCase().equals("true")){ this.testing = true; } else { System.err.println("Config param \"testing\" should be either true or false! Setting to default false."); this.testing = false; } if (pubProps.getProperty("logWriting", "true").toLowerCase().equals("true")){ this.logWriting = true; } else if (pubProps.getProperty("logWriting").toLowerCase().equals("false")){ this.logWriting = false; } else { System.err.println("Config param \"logWriting\" should be either true or false! Setting to default true."); this.logWriting = true; } } catch (Exception e){ e.printStackTrace(); System.exit(-1); } log = new LogWriter(this.myName + "_subscriberLog.txt", logWriting, testing); log.writeToLog("Publisher name: " + this.myName, true); log.writeToLog("Publisher broker port: " + this.myBrokerPort, true); log.writeToLog("Publisher broker IP: " + this.myBrokerIP, true); log.writeToLog("", true); } /** * Used for connecting publisher to broker */ @Override public void connect() { if (connected){ log.writeToLog("Connect request received while being connected. Ignored."); return; } try { socket = new Socket(this.myBrokerIP, this.myBrokerPort); this.myPort = socket.getLocalPort(); out = new ObjectOutputStream(socket.getOutputStream()); out.flush(); } catch (UnknownHostException ex) { log.writeToLog("Connecting failed - Unknown Broker Host or Port: " + ex); try {socket.close();} catch (Exception e){} socket = null; out = null; return; } catch (IOException ex) { log.writeToLog("Failed to open stream to the Broker: " + ex); try {socket.close();} catch (Exception e){} socket = null; out = null; return; } Message connectMessage = new PublisherRegisterMessage(myName, this.getId()); this.sendMessage(connectMessage); //TODO FIXME no confirmation is waited for... it is just assumed the connection is ok. log.writeToLog("Connected to Broker " + myBrokerIP + " " + myBrokerPort); this.connected = true; Iterator<Publication> iteratorPublication = outboxPubs.iterator(); while (iteratorPublication.hasNext()) { publish(iteratorPublication.next()); iteratorPublication.remove(); } } /** * Used for handling negative response message (reconnecting to broker) * * @param brokerIP IP address of reconnecting broker * @param brokerPort Port number of reconnecting broker */ @Override public void reconnect(String brokerIP, int brokerPort) { if (connected){ log.writeToLog("Disconnecting from broker (part of reconnect)."); disconnectFromBroker(); } this.myBrokerIP = brokerIP; this.myBrokerPort = brokerPort; this.connect(); } /** * Reconnect to last connected broker * */ @Override public void reconnect() { if (connected){ log.writeToLog("Disconnecting from broker (part of reconnect)."); disconnectFromBroker(); } this.connect(); } /** * Used for disconnecting from broker */ @Override public void disconnectFromBroker() { if (connected) { Message disconnectMessage = new PublisherDisconnectMessage(); this.sendMessage(disconnectMessage); //TODO FIXME no confirmation is waited for... it is just assumed the communication went ok terminateConnection(); log.writeToLog("Disconnected from broker!"); } else { log.writeToLog("Cannot disconnect from broker because not connected."); } } /** * For terminating the connection... * closes to outSocket and sets everything to null */ private void terminateConnection(){ try { socket.close(); //the receiving scket will be closed from the server side } catch (Exception e){ //ignore } socket = null; out = null; this.connected = false; } /** * Publishing publication from XML file * * @param fileName Name of input file * @return returns publication UUID */ public UUID publishFromXMLFile(String fileName) { return publish(fileName, ""); } /** * Publishing publication from string * * @param inputString string containing XML publication * @return returns publication UUID */ public UUID publishFromXMLString(String inputString) { return publish("", inputString); } /** * Publishing publication from XML file or content (not both - one has to be * empty string ("")) * * @param fileName Name of input file * @param inputString Name of input String * @return returns publication UUID */ public UUID publish(String fileName, String inputString) { ReadingWritingXML input = new ReadingWritingXML(fileName, inputString); if (fileName.equalsIgnoreCase("")) { input.readString(); } else { input.readFile(); } Publication pub = input.createPublication(); this.publish(pub); return pub.getId(); } /** * Used for publishing new publication * * @param publication Publication to be published */ public void publish(Publication publication) { if (connected) { Message sendMsg = new PublishMessage((HashtablePublication)publication, false); this.sendMessage(sendMsg); log.writeToLog("Publication "+publication+" sent to broker."); //TODO no confirmation is waited for here... activePubs.add(publication); allPubs.add(publication); } else { outboxPubs.add(publication); allPubs.add(publication); log.writeToLog("Publication "+publication+" put in outbox because not connected to broker."); } } /** * Used for unpublishing old publication * * @param publication Publication to be unpublished */ public void unpublish(Publication publication) { if (activePubs.contains(publication)) { if (connected) { Message sendMsg = new PublishMessage(publication, true); this.sendMessage(sendMsg); log.writeToLog("Unpublication request sent to broker."); } activePubs.remove(publication); } else if (outboxPubs.contains(publication)){ outboxPubs.remove(publication); log.writeToLog("Publication unpublished from outbox. No need to contact the broker."); } else { log.writeToLog("Unpublication impossible because publication is no longer active."); } } /** * Used for sending messages to broker * * @param sendMasg message to be sent */ protected void sendMessage(Message sendMasg) { try { out.writeObject(sendMasg); out.flush(); } catch (Exception e1){ log.error("Message "+sendMasg+" not sent. Disconnecting because of connection problems."); e1.printStackTrace(); terminateConnection(); } } public boolean isConnected() { return connected; } public boolean isLogWriting() { return logWriting; } public boolean isTesting() { return testing; } }