All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.paho.sample.utility.MQTTFrame Maven / Gradle / Ivy

The newest version!
/********************************************************************************
 * Copyright (c) 2002, 2013 IBM Corp.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution. 
 *
 * The Eclipse Public License is available at 
 *    http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at 
 *   http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Description: Contains the main method for starting the MQTT swing user interface
 *
 * Contributors:
 *    Ian Harwood, Ian Craggs - initial API and implementation and/or initial documentation
 ********************************************************************************/

package org.eclipse.paho.sample.utility;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Properties;
import java.util.StringTokenizer;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.EtchedBorder;

import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;

/**
 * This class is the controlling class for the application. It contains the main method
 * for launching the Swing GUI and itcontrols the MQTT connection.
 */
public class MQTTFrame implements ActionListener, MqttCallback, Runnable {
	private JPanel connPanel = new JPanel();
	private JPanel pubPanel = new JPanel();
	private JPanel subPanel = new JPanel();
	private PubPanel pubPanelContr;
	private SubPanel subPanelContr;
	
	// Main Components
	private ConnOpts   optionsComp = null;
	private MQTTHist historyComp = null;
	private JPanel     mqttComp = null;

	private JComboBox ipAddress;
	private JComboBox port;
	private LED       led;
	private JButton   connect;
	private JButton   disconnect;
	private MqttClient mqtt = null;
    private MqttConnectOptions opts = null;
	private boolean connected = false;
	private boolean traceEnabled = false;
	private Object    connLostWait = new Object(); // Object to coordinated ConnectionLost and disconnect threads if
                	                                // disconnect is hit during connectionLost
	private JFrame frame= null;
	
	/**
	 *  Constant controlling the display of JTextFields
	 */
    protected static final Dimension TEXT_FIELD_DIMENSION = new Dimension( 1000, 20 );
	/**
	 *  Constant controlling the display of JComboBoxes
	 */
    protected static final Dimension DROP_DOWN_DIMENSION = new Dimension(35, 20);
    protected static final Insets TEXT_MARGINS = new Insets(3,3,3,3);
    protected static final int FRAME_WIDTH = 375;
    protected static final int FRAME_HEIGHT = 450;

    // The name of the properties file
    private final static String PROP_FILE = "mqtt.properties";
    private final static String PROP_DELIM = ";";

    // Other constants
    private static final String DEFAULT_IP_ADDRESS = "127.0.0.1";
    private static final String DEFAULT_PORT_NUMBER = "1883";
	/**
	 * Constructor for MQTTFrame
	 */
	public MQTTFrame() {		
		super();
	}


    /**
     * The main method for launching the GUI. The main method takes no arguments.
     * 
* This method attaches a WindowListener to the window to detect windowClosing events. When * such an event is detected then various parameters are gathered from the GUI and written * to a properties file before the application exits. * @param args No arguments are required. */ public static void main(String[] args) { JFrame theFrame = null; final MQTTFrame view = new MQTTFrame(); theFrame = view.getJFrame(); theFrame.setSize( MQTTFrame.FRAME_WIDTH, MQTTFrame.FRAME_HEIGHT ); theFrame.setLocation(150, 50); theFrame.setResizable(true); view.init( theFrame.getContentPane() ); theFrame.setVisible(true); theFrame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { // Don't cleanly disconnect when the window is closed. // This allows things like Last Will & Testament to be easily tested by // closing the window to unexpectedly terminate the MQTT session. //view.disconnect(); // Write the properties to disk before exiting FileOutputStream propFile = null; Properties props = new Properties(); try { propFile = new FileOutputStream( PROP_FILE ); // First populate the properties object // IPADDRESS props.setProperty("IPAddressList", view.constructPropertyValue("IPAddressList") ); // IPPORT props.setProperty("IPPortList", view.constructPropertyValue("IPPortList") ); // CLIENTID props.setProperty("ClientId", view.constructPropertyValue("ClientId") ); // PERSISTENCE ENABLED props.setProperty("Persistence", view.constructPropertyValue("Persistence") ); // PERSISTENCE DIRECTORY props.setProperty("PersistenceDir", view.constructPropertyValue("PersistenceDir") ); // Now write to disk props.store( propFile, "MQTT Utility properties" ); } catch(Exception fe) { // If we cannot create a properties file then don't worry } System.exit(0); } }); } /** * @return This MQTTFrame's JFrame object reference. */ private JFrame getJFrame() { if ( frame == null ) { frame = new JFrame(); } return frame; } /** * This method builds all the components to add to the * content pane of the JFrame window. It builds the connect panel itself and delegates building * of the publish and subscribe panels to the PubPanel and SubPanel objects respectively. * It also loads the properties file and gives the window a Windows look and feel. * @param contentPane - The content pane of the JFrame window. */ protected void init( Container contentPane ) { FileInputStream propFile = null; Properties props = new Properties(); try { UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } catch ( Exception ex) { // Don't worry if we can't set the look and feel } // Does a properties file exist from which the GUI can be populated? try { propFile = new FileInputStream( PROP_FILE ); props.load( propFile ); } catch(Exception fe) { // If we can't find a properties file then don't worry propFile = null; } // Now build the GUI components setTitleText(""); connPanel.setLayout(new GridLayout(2, 1)); connPanel.setBorder( new EtchedBorder() ); // Build the main components to add to the tabbed pane. mqttComp = new JPanel( new BorderLayout() ); historyComp = new MQTTHist( getJFrame(), optionsComp ); // Add the panel which handles connecting and disconnecting from the broker mqttComp.add(connPanel, BorderLayout.NORTH); // Add the panels for publish and subscribe to a JSplitPane // The JSplitPane allows the panels to be resized evenly, allows the user to resize the // panels manually and provides a good layout for two similar panels JSplitPane pubsub = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, subPanel, pubPanel ); pubsub.setOneTouchExpandable(true); // Allow either panel to be expanded to full size easily pubsub.setDividerSize(10); // Set the divider size large enough to display the one touch expandable arrows pubsub.setResizeWeight(0.5); // Resize evenly // Add the pubsub JSplitPane to the outer frame mqttComp.add( pubsub, BorderLayout.CENTER ); ipAddress = new JComboBox(); ipAddress.setPreferredSize( new Dimension(125,20) ); ipAddress.setEditable( true ); // Load any TCP/IP address info from the config file getProperties( props, ipAddress, "IPAddressList", DEFAULT_IP_ADDRESS ); port = new JComboBox(); port.setPreferredSize( new Dimension(65,20) ); port.setEditable( true ); // Load any TCP/IP port info from the config file getProperties( props, port, "IPPortList", DEFAULT_PORT_NUMBER ); // Create the options panel optionsComp = new ConnOpts( this, props ); // Add an LED to indicate the connection state led = new LED(); led.setRed(); new Thread(led).start(); connect = new JButton( "Connect" ); disconnect = new JButton( "Disconnect" ); disconnect.setEnabled(false); JButton history = new JButton("History"); connect.addActionListener(this); disconnect.addActionListener(this); history.addActionListener(this); JPanel text = new JPanel(); text.add( new JLabel("Broker TCP/IP address: ") ); text.add( ipAddress ); text.add( port ); JPanel buttons = new JPanel(); buttons.setLayout( new BoxLayout( buttons, BoxLayout.X_AXIS ) ); buttons.add( new JSeparator( SwingConstants.VERTICAL ) ); buttons.add( led ); buttons.add( connect ); buttons.add( new JLabel(" ") ); buttons.add( disconnect ); buttons.add( new JSeparator( SwingConstants.VERTICAL ) ); buttons.add( history ); buttons.add( new JSeparator( SwingConstants.VERTICAL ) ); connPanel.add( text ); connPanel.add( buttons ); pubPanelContr = new PubPanel( pubPanel, this ); subPanelContr = new SubPanel( subPanel, this ); if ( propFile != null ) { try { propFile.close(); } catch(Exception e) { // Don't worry if we can't close the properties file } } // Now construct the tabbed pane JTabbedPane tabbedGui = new JTabbedPane(); tabbedGui.addTab( "MQTT", mqttComp ); tabbedGui.addTab( "Options", optionsComp ); contentPane.add( tabbedGui ); } /** * A simple wrapper for the MQTT publish method. This method is invoked as a result of the * publish button being pressed. If a problem is detected then an exception is thrown and an error * message is displayed in the window title bar or in a separate dialog box. * @param topic The topic on which the data will be published. * @param message The data to be published * @param qos The Quality of Service at which the publication should be delivered. * @param retained Is this a retained publication or not? */ public void publish( String topic, byte[] message, int qos, boolean retained ) throws Exception { setTitleText( "" ); if ( connected ) { try { mqtt.getTopic(topic).publish( message, qos, retained ); } catch ( MqttException ex ) { setTitleText( "MQTT publish exception !" ); JOptionPane.showMessageDialog( frame, ex.getClass().getName() + "\n" + ex.getMessage(), "MQTT Publish Exception", JOptionPane.ERROR_MESSAGE ); throw ex; } } else { setTitleText( "MQTT client not connected !" ); throw new Exception( "MQTT client not connected" ); } } /** * A wrapper for the MQTT disconnect method. * As well as disconnecting the protocol this method enables / disables buttons * as appropriate when in disconnected state. It also sets the correct colour of the indicator LED. */ public void disconnect() { connected = false; // Notify connectionLost to give up. It may be running.. synchronized(connLostWait) { connLostWait.notify(); } // Disconnect from the broker if ( mqtt != null ) { try { mqtt.disconnect(); } catch ( Exception ex ) { setTitleText( "MQTT disconnect error !" ); ex.printStackTrace(); System.exit(1); } } // Set the LED state correctly // If the led is flashing then turn it off // This only occurs if disconnect is hit during connection lost if ( led.isFlashing() ) { led.setFlash(); } led.setRed(); setConnected( false ); synchronized(this) { // Grab the log synchronisation lock writeLogln("WebSphere MQ Telemetry transport disconnected" ); } } /** * A wrapper for the MQTT connect method. If the ip address, port number or persistence flag * has changed since the last time then a new MqttClient object is required. If these values haven't changed then * any previously created object can be used.

* Check whether Last Will & Testament is required and call the appropriate connect method. The only persistence implementation supported at the moment is * MqttFilePersistence. * @param ipAddr The IP address or hostname to connect to. * @param port The IP port number to connect to. * @param usePersistence Is persistence required? */ public void connect( String connStr, boolean usePersistence ) throws MqttException { // Connect to the broker // If we have a MqttClient object and the new ip address // or port number is not equal to the previous, or the persistence flag changes between // off and on then we need a new object. if ( (mqtt != null) && (!connStr.equals(mqtt.getServerURI()) /*|| (usePersistence != (mqtt.getPersistence() != null) )*/ ) ) { //mqtt.terminate(); mqtt = null; } if ( mqtt == null ) { MqttClientPersistence persistence = null; if ( usePersistence ) { persistence = new MqttDefaultFilePersistence( optionsComp.getPersistenceDirectory() ); } mqtt = new MqttClient( connStr, optionsComp.getClientID(), persistence ); mqtt.setCallback( this ); // Carry the trace setting over to the new MqttClient object if ( traceEnabled ) { startTrace(); } } // Set the retry interval for the connection //mqtt.setRetry( optionsComp.getRetryInterval() ); opts = new MqttConnectOptions(); opts.setCleanSession(optionsComp.isCleanSessionSelected()); opts.setKeepAliveInterval(optionsComp.getKeepAlive()); if ( optionsComp.isLWTTopicSet() ) { opts.setWill(mqtt.getTopic(optionsComp.getLWTTopic()), optionsComp.getLWTData().getBytes(), optionsComp.getLWTQoS(), optionsComp.isLWTRetainSelected()); } mqtt.connect(opts); } /** * This method is called when either the subscribe or unsubscribe buttons are pressed. It performs the * MQTT subscribe or unsubscribe and writes an entry in the history log if the history window is open.
* Any exceptions are caught and displayed in a dialog box. * @param topic The topic to subscribe to * @param qos The maximum Quality of Service at which to receive publications * @param sub Is this a subscribe or unsubscribe operation? (true if subscribe). */ public void subscription( String topic, int qos, boolean sub ) { setTitleText( "" ); if ( connected ) { try { String[] theseTopics = new String[1]; int[] theseQoS = new int[1]; theseTopics[0] = topic; theseQoS[0] = qos; synchronized(this) { // Grab the log synchronisation lock if ( sub ) { writeLogln( " --> SUBSCRIBE, TOPIC:" + topic + ", Requested QoS:" + qos ); } else { writeLogln( " --> UNSUBSCRIBE, TOPIC:" + topic ); } } if ( sub ) { mqtt.subscribe( theseTopics, theseQoS ); } else { mqtt.unsubscribe( theseTopics ); } } catch ( Exception ex ) { setTitleText( "MQTT subscription exception caught !" ); JOptionPane.showMessageDialog( frame, ex.getMessage(), "MQTT Subscription Exception", JOptionPane.ERROR_MESSAGE ); } } else { setTitleText( "MQTT client not connected !" ); } } /** Invoked by actionPerformed when connect is pressed. * This allows actionPerformed to return and paint the window. This thread * then does the MQTT connect to the broker.
* This method also ensures that the LED colour is set correctly and writes * an entry to the history dialog if it is open. */ public void run() { int rc = -1; // Connect to the broker String ipAddr = (String)ipAddress.getSelectedItem(); String portNum = (String)port.getSelectedItem(); String connStr = ""; try { // If the entry in the IP Address drop down list contains '://' then assume // the connection has been explicitly entered as tcp://ip_address:port or local://broker_name. // Otherwise read the ip address and port number from their respective drop downs. if ( ipAddr.indexOf("://") < 0 ) { connStr = "tcp://" + ipAddr + ":" + portNum; } else { connStr = ipAddr; } connect( connStr, optionsComp.isPersistenceSelected() ); // Successful connect(no exception). Remember the ipAddress and port in the drop downs updateComboBoxList( ipAddress, ipAddr ); updateComboBoxList( port, portNum ); connected = true; led.setGreen(); setConnected( true ); } catch( NumberFormatException nfe ) { JOptionPane.showMessageDialog( frame, "Invalid port number !", "MQTT Connect Exception", JOptionPane.ERROR_MESSAGE ); } catch ( MqttException mqe ) { setTitleText( "MQTT connect failed !" ); Throwable e = mqe.getCause(); String msg = ""; if ( e == null ) { e = mqe; } else if ( mqe.getMessage() != null ) { msg += mqe.getMessage() + "\n"; } msg += e; JOptionPane.showMessageDialog( frame, msg, "MQTT Connect Exception", JOptionPane.ERROR_MESSAGE ); e.printStackTrace(); } catch ( Exception ex ) { setTitleText( "MQTT connect failed !" ); JOptionPane.showMessageDialog( frame, ex, "MQTT Connect Exception", JOptionPane.ERROR_MESSAGE ); ex.printStackTrace(); } if ( !connected ) { led.setRed(); setConnected( false ); } synchronized(this) { // Grab the log synchronisation lock if ( connected ) { writeLogln("WebSphere MQ Telemetry transport connected to " + mqtt.getServerURI() ); } else { writeLogln("ERROR:WebSphere MQ Telemetry transport failed to connect to " + connStr ); } } } /** * Implement the ActionListener interface and catch user interface events. Button pressed events handled are as follows: *

  • Connect - Check the client is not already connected, set the LED colour to amber to indicate connect is in progress and * start a thread to do the connect. *
  • Disconnect - Check the client is connected and then disconnect the MQTT protocol *
  • Otions - If the options dialog is opened then reset it's size and position to default values before making it visible *
* @param e The action event to process. */ public void actionPerformed( ActionEvent e) { setTitleText( "" ); if ( e.getActionCommand().equals("Connect") ) { // When the connect button is pressed we are either connected or not connected // If we are connected then say so. // If we are not connected then // 1. Set the LED to Amber, the state to connecting and start a thread to do the actual connect. // This allows the GUI thread to return and paint the window correctly if ( connected ) { // Already connected setTitleText( "MQTT session already active !" ); } else { // Initialise the GUI prior to connecting by setting the LED to amber. // Start a thread to do the connect. connect.setEnabled(false); led.setAmber(); connected = false; new Thread(this).start(); } } else if ( e.getActionCommand().equals("Disconnect") ) { if ( connected ) { // Disconnect from the broker disconnect(); } else { setTitleText( "MQTT client not connected !" ); } } else if ( e.getActionCommand().equals("History") ) { historyComp.enableHistory(); } } /** * This method accepts a string on text and displays it in the window's title bar. * @param extraText The text to be appended to some default words and displayed. */ // Synchronized as this may also be called on the connectionLost thread, which is // created by the Java MQTT Client public synchronized void setTitleText( String extraText ) { if ( extraText.equals("") ) { frame.setTitle( "Paho MQTT Utility" ); } else { frame.setTitle( "Paho MQTT Utility - " + extraText ); } } /** * The method is part of the MqttSimpleCallback interface *
In the event of the MQTT connection being broken the LED is set to colour amber and made to flash. * The code then keeps trying to reconnect until either a successful * reconnect occurs or the disconnect button is pressed. Finally the LED is stopped flashing and set to * green or red depending upon whether the connect was successful or not. */ public void connectionLost(java.lang.Throwable cause) { int rc = -1; // Flip the LED to Amber and set it flashing led.setAmber(); led.setFlash(); setTitleText( "Connection Lost!....Reconnecting" ); synchronized(this) { // Grab the log synchronisation lock writeLogln( "MQTT Connection Lost!....Reconnecting to " + mqtt.getServerURI() ); } try { // While we have failed to reconnect and disconnect hasn't // been called by another thread retry to connect while ( (rc == -1) && connected ) { try { synchronized(connLostWait) { connLostWait.wait(10000); } } catch (InterruptedException iex) { // Don't care if we are interrupted } synchronized(this) { // Grab the log synchronisation lock if ( connected ) { writeLog( "MQTT reconnecting......" ); try { connect( mqtt.getServerURI(), optionsComp.isPersistenceSelected() ); rc = 0; } catch (MqttException mqte) { // Catch any MQTT exceptions, set rc to -1 and retry rc = -1; } if ( rc == -1 ) { writeLogln( "failed" ); } else { writeLogln( "success !" ); } } } } // Remove title text once we have reconnected setTitleText( "" ); } catch (Exception ex) { setTitleText( "MQTT connection broken !" ); ex.printStackTrace(); disconnect(); //throw ex; } finally { // Set the flashing off whatever happens if ( led.isFlashing() ) { led.setFlash(); // Flash off } } // If we get here and we are connected then set the led to green if ( connected ) { led.setGreen(); setConnected( true ); } else { led.setRed(); setConnected( false ); } } /** * The method is part of the MqttCallback interface
* Pass the message as is to the SubPanel object which will display it. */ public void messageArrived( String topic, MqttMessage message) { try { subPanelContr.updateReceivedData( topic, message.getPayload(), message.getQos(), message.isRetained() ); } catch (Exception e) { } } public void deliveryComplete( IMqttDeliveryToken token ) { } /** * If a new topic is typed into the publish topic box then the * subscribe topic box needs to be updated. * @param topicName The topic name to add to the subscribe topic list */ public void updateSubscribeTopicList( String topicName ) { subPanelContr.updateTopicList( topicName ); } /** * If a new topic is typed into the subscribe topic box then the * publish topic box needs to be updated. * @param topicName The topic name to add to the publish topic list */ public void updatePublishTopicList( String topicName ) { pubPanelContr.updateTopicList( topicName ); } /** * This method handles string items in JComboBox drop downs. It checks * to see if an item already exists in the list. If it doesn't, then it updates the list. * @param list The JComboBox object to update * @param itemName The value to add into the list if it doesn't already exist * @return True if the item was added to the list */ public boolean updateComboBoxList( JComboBox list, String itemName ) { int listCount = list.getItemCount(); boolean found = false; if ( itemName == null ) { return false; } for( int i=0; i 0) { retString = ""; } else { retString = DEFAULT_IP_ADDRESS; } for( int i=0; i < numAddrs; i++ ) { retString += ipAddress.getItemAt(i); // Don't add a delimiter after the last token if ( i != numAddrs - 1 ) { retString += PROP_DELIM; } } } else if ( prop.equals("IPPortList") ) { int numPorts = port.getItemCount(); if (numPorts > 0) { retString = ""; } else { retString = DEFAULT_PORT_NUMBER; } for( int i=0; i < numPorts; i++ ) { retString += port.getItemAt(i); // Don't add a delimiter after the last token if ( i != numPorts - 1 ) { retString += PROP_DELIM; } } } else if ( prop.equals("ClientId") ) { retString = optionsComp.getClientID(); } else if ( prop.equals("Persistence") ) { retString = String.valueOf( optionsComp.isPersistenceSelected() ); } else if ( prop.equals("PersistenceDir") ) { retString = optionsComp.getPersistenceDirectory(); } return retString; } /** * If we are not connected then disable the disconnect, publish, * subscribe, and unsubscribe buttons. Enable connect. * If we are connected then do the inverse. * @param b True if connected, false otherwise. */ private void setConnected( boolean b ) { pubPanelContr.enableButtons( b ); subPanelContr.enableButtons( b ); disconnect.setEnabled( b ); connect.setEnabled( !b ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy