Martin Gale’s blog

web 2.0, extended integration, pervasive messaging and other technical thoughts

Posts Tagged ‘expeditor’

Sample composite application in Lotus Expeditor 6.2

Posted by Martin on June 22, 2009

One of the key features of Lotus Expeditor and Lotus Notes 8.5 is the Composite Applications Environment (CAE). The CAE provides a framework for integration “on the glass” of desktop applications such as native applications, terminal emulation or browser-based applications that are not otherwise easily integrated. This posting shows a very simple example of how to use the tools provided in the Lotus Expeditor 6.2 Client for Desktop to integrate together a set of disparate browser-based applications by way of illustration.

Preparing your environment

There are two possible ways to configure a composite application in the CAE — via extension points in plug-ins created using the Expeditor Toolkit or via GUI tools provided for the Expeditor Desktop Client itself. This sample uses the GUI method to create the composite application.

The GUI tooling is not installed by default with the Expeditor Desktop Client, but is supplied in the install media. You will need to install the required feature from the update site supplied with the Client inside the desktop\updates\platform folder.

Creating a new Composite Application

Once the CAE feature has been installed, you can begin creating your Composite Application using the File menu.

image

Selecting New Composite Application… will launch a modal dialog box prompting you for a filename to store your Composite Application. In this case we’ve chosen to store the Composite Application under the name Radio 1 Sample.

image

Having clicked OK we then see a blank Composite Applications page as shown below.

image

The scenario we are going to create is a very simple one whereby we take two separate web applications from different vendors and integrate data between them within a simple task flow. To begin editing the page, we follow the Actions -> Edit Application menus. We see the editing screen appear.

image

Configuring containers

The CAE works on the principle that the applications are added into the page using a set of generic containers. These containers provide a common interface around the various different desktop client technologies such that they can ultimately be wired together in a standardised fashion. Since the applications we are integrating are browser based, we will use the Browser Container shown on the Component Palette. Dragging the container onto the page creates a default configuration of the container.

image

We will modify this default configuration to reflect our application. We will use data contained within the BBC Radio 1 Album Chart to pre-populate a search in Amazon for the name of the top album. I have deliberately chosen these since they are two public sites over which I could not reasonably integrate myself in any other fashion.

We will start by configuring a container for the BBC Radio 1 web site as the source of the data. To start this process we select the Edit Component Properties option from the context menu shown below.

image

A modal dialog box then appears allowing various settings about the container. We shall first change the title and description to something more meaningful.

image

Now, we will configure the details of the starting page for the container and also define the data properties that the container will expose to the environment for composition.

image

You will see that not only have we changed the initialURL property but we have created two further properties albumArtist and albumTitle. In the case of the latter two we have ticked the Wireable box. This tells the CAE that these are data properties on this container that can be integrated (or wired) with other containers.

We now need to specify how and when albumArtist and albumTitle are populated by the container. The CAE has the notion of a landmark with an associated set of events for containers. A landmark indicates a specific logical point in the application. In the case of a web browser, we can specify either a URL or the title of the page in question. For our example we are interested specifically in the Album Chart so we shall set a landmark of the Album Chart page using its URL. We select the Landmarks tab on the dialog, then select URL as the Type of Landmark and finally click the Add Landmark button.

 

image

For our landmark we can add a set of event handlers associated with it. This tells the CAE what to do when the particular landmark is reached. For a web browser, the most common event is the Content Complete event which is fired when the web page has completed loading. In response to an event we can tell the CAE to interrogate the container to set the data in one of the Wireable properties we defined earlier. In this case we will specify the XPath within the page in the browser of the HTML element containing the data elements we are interested in. A quick and simple way of finding the XPath of an HTML node is to use the Firebug utility for Firefox.

image

Now we can specify our event handler. We will add two operations for the Content Complete event on the page to copy the values contained in the HTML into the attributes we defined earlier. We click the Add Event button and then Add Operation with Content Complete set as the Event.

image

The above Operations effectively the following:

  • When the page completes loading, publish the value held at the XPath /html/body/div/div[2]/div[2]/div[4]/div/table/tbody/tr[2]/td/table/tbody[2]/tr/td[4]/h4 into the property named albumArtist.
  • When the page completes loading, publish the value held at the XPath /html/body/div/div[2]/div[2]/div[4]/div/table/tbody/tr[2]/td/table/tbody[2]/tr/td[4]/h4 into the property named albumTitle.

Note the xpath: prefix indicating to the Browser Container that the meta-data that follows is a valid XPath string. Note that this meta-data is container specific, other containers may have different schemes.

Pressing OK on the dialog box will save the changes and present the modified container in the editor.

image

Now we need to configure a container for Amazon in the same way. We will now repeat the process above, note that you can drag your new container from the palette onto the page and attach it to a convenient point on the page (e.g. the bottom edge so the screen is split horizontally). This time, we will set the intialURL property to http://www.amazon.co.uk for the Amazon home page and a description and title to that effect. We will also do the following:

  1. Add a Wireable property called searchQuery.
  2. Create a Title landmark for the page with the title “Amazon.co.uk Music: over two million music items, including chart CDs, back-catalogue, vinyl, cassettes” (omitting the speech marks). Title landmarks are useful when the URLs can vary e.g. some sites use a URL rewriting mechanism for session affinity.
  3. Add a Content Complete event with a single Operation to Receive from the searchQuery property and put the value into the XPath //*[@id="twotabsearchtextbox"].

A snapshot of the Browser Container settings for the Amazon container is shown below.

image

Notice that in this case we choose to Receive (rather than Publish) as we will have incoming data to be plugged into the Amazon page rather than data to be propagated out as we had before. We now have two containers configured on our page.

image

Wiring the containers into a composite

We now need to wire the two containers together. Note that at this point we will no longer be talking about the specifics of how the containers give and receive data (i.e. no more talk of XPaths). This is an important feature of the CAE since it means that relatively few technical skills are required to assemble a set of containers once they have been configured. At the wiring level, the user deals only in terms of properties and wires.

image

From the context menu shown above we select the Wiring menu option. We then see the Wiring view showing the two containers we have configured.

 image

Notice how for each container we configured earlier that each Wireable property is shown on the graphical representation of the container.

What we want to do is wire the albumArtist property into the searchQuery such that when the events we configured for the container fire, the data will flow along the wire from the BBC Radio 1 Charts container into the Amazon (UK) container. We do this simply by clicking and dragging the albumArtist property and releasing it over the searchQuery property. A blue arrow appears whilst the drag/drop is in progress, once the drop is complete we see the following:

image

We complete our composite application by clicking OK on the Wiring screen, then the File -> Save and Close menu options on the editor screen to save the changes to the Composite Application. When we return to the Expeditor desktop, we now see a tab with our Composite Application contained within it.

image

Testing the application

We are now ready to test the application. To show the integration working, we simply navigate the BBC page to the album chart using the link within the page to http://www.bbc.co.uk/radio1/chart/albums.shtml and then click through on Amazon to the Music Department. We should see the following screens.

image

As you can see when we click onto the Music Department, the title of the top album in the Radio 1 chart has been extracted from the HTML and populated into the input field on the Amazon page. Our composite application is complete and working.

Posted in Snippets | Tagged: , , , , , | 1 Comment »

AES 256 encryption using Java and Lotus Expeditor 6.2

Posted by Martin on March 24, 2009

In a recent engagement, one of the requirements for the application was the ability to encrypt the payload of an MQTT message using 256 bit Advanced Encryption Standard (AES) encryption, where the key was created using a combination of a user name and a pre-shared key. This was to be accomplished using the Lotus Expeditor Device JRE profile running on a desktop PC.

I’ve captured how I did this for posterity here, it’s basically the standard Java Cryptography Architecture (JCA) so should be generally useful in other domains too. I’ve used a really simple OSGi bundle that performs the cryptography in the start() method to prove the point within the Expeditor toolkit.

Creating a the encryption key

In this scenario, I needed to construct a 256 bit key derived from a user name and pre-shared key. Java provides the SecretKeySpec class to enable the provision of application specific data within a key.

// My user name
byte[] loginId = "galem".getBytes();
// A sample pre-shared key -- this might be something like an account number
// or employee number or such like.
byte[] preSharedKey = "ACME-1234ACME-1234ACME-1234".getBytes(); 
byte[] encodedKey = new byte[loginId.length + preSharedKey.length];

System.arraycopy(loginId, 0, encodedKey, 0, loginId.length);
System.arraycopy(preSharedKey, 0, encodedKey, loginId.length, preSharedKey.length);

// The SecretKeySpec provides a mechanism for application-specific generation
// of cryptography keys for consumption by the Java Crypto classes.

// Create a key specification first, based on our key input.
SecretKey aesKey = new SecretKeySpec(encodedKey, "AES");

Encrypting the data

Now we have the key, we can now create a Crypto class to encrypt the data. I’ve output the result to the console to show a “before and after” view.

// Create a Cipher for encrypting the data using the key we created.
Cipher encryptCipher = Cipher.getInstance("AES");
// Initialize the Cipher with key and parameters
encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey);

// Our cleartext
String clearString = "This is another example";
byte[] cleartext = clearString.getBytes();

System.out.println("Plain text: "+clearString);

// Encrypt the cleartext
byte[] ciphertext = encryptCipher.doFinal(cleartext);

System.out.println("Encrypted: "+new String(ciphertext));

Decrypting the data back again

Finally, to show the encrypted data can be returned to its original form, we’ll reverse the process.

// Now decrypt back again...
// Decryption cipher
Cipher decryptCipher = Cipher.getInstance("AES");
// Initialize PBE Cipher with key and parameters
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey);

// Decrypt the cleartext
byte[] deciphertext = decryptCipher.doFinal(ciphertext);

System.out.println("Decrypted: "+new String(deciphertext));

Full listing

package com.ibm.issw.sample.crypto;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.eclipse.core.runtime.Plugin;
import org.osgi.framework.BundleContext;

/**
 * The activator class controls the plug-in life cycle
 */
public class Activator extends Plugin {

	// The plug-in ID
	public static final String PLUGIN_ID = "com.ibm.issw.sample.crypto";

	// The shared instance
	private static Activator plugin;

	/**
	 * The constructor.
	 */
	public Activator() {
	}

	/**
	 * @see org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext)
	 */
	public void start(BundleContext context) throws Exception {
		super.start(context);
		plugin = this;

		try {

		    // In this example we will just use a combination of a user name and
		    // a pre-shared key.

		    // My user name
		    byte[] loginId = "galem".getBytes();
		    // A sample pre-shared key -- this might be something like an account number
		    // or employee number or such like.
		    byte[] preSharedKey = "ACME-1234ACME-1234ACME-1234".getBytes();

		    byte[] encodedKey = new byte[loginId.length + preSharedKey.length];

		    System.arraycopy(loginId, 0, encodedKey, 0, loginId.length);
		    System.arraycopy(preSharedKey, 0, encodedKey, loginId.length, preSharedKey.length);

		    // The SecretKeySpec provides a mechanism for application-specific generation
		    // of cryptography keys for consumption by the Java Crypto classes.

		    // Create a key specification first, based on our key input.
		    SecretKey aesKey = new SecretKeySpec(encodedKey, "AES");

		    // Create a Cipher for encrypting the data using the key we created.
		    Cipher encryptCipher = Cipher.getInstance("AES");
		    // Initialize the Cipher with key and parameters
		    encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey);

		    // Our cleartext
		    String clearString = "This is another example";
		    byte[] cleartext = clearString.getBytes();

		    System.out.println("Plain text: "+clearString);

		    // Encrypt the cleartext
		    byte[] ciphertext = encryptCipher.doFinal(cleartext);

		    System.out.println("Encrypted: "+new String(ciphertext));

		    // Now decrypt back again...
		    // Decryption cipher
		    Cipher decryptCipher = Cipher.getInstance("AES");
		    // Initialize PBE Cipher with key and parameters
		    decryptCipher.init(Cipher.DECRYPT_MODE, aesKey);

		    // Decrypt the cleartext
		    byte[] deciphertext = decryptCipher.doFinal(ciphertext);

		    System.out.println("Decrypted: "+new String(deciphertext));

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext)
	 */
	public void stop(BundleContext context) throws Exception {
		plugin = null;
		super.stop(context);
	}

	/**
	 * @return the shared instance
	 */
	public static Activator getDefault() {
		return plugin;
	}

}

Posted in Snippets | Tagged: , , , , , , | Leave a Comment »

Adding items to the Expeditor 6.2 Desktop launch menu

Posted by Martin on January 20, 2009

A nice way of kicking off either external applications or launching a URL in the browser from an Expeditor application is to add menu items to the launcher (the “Open” button in the standard Expeditor Desktop client). This is particularly useful when building a locked-down desktop in Expeditor (i.e. where we want to restrict what the user can do) or even for a demo rig where we wish to use the Expeditor Client as the focal point for the application.

It’s pretty easy to do with extension points, here’s an example of one I created earlier:

<?xml version=”1.0″ encoding=”UTF-8″?>
<?eclipse version=”3.2″?>
<plugin>
<extension point=”com.ibm.rcp.ui.launcherSet”
id=”com.ibm.issw.acme.app.demorig.launchers”>
<LauncherSet
id=”com.ibm.issw.acme.app.demorig.launcherItems”
label=”ACME Application Demo”>
<nativeProgramLaunchItem
environmentMode=”APPEND”
id=”com.ibm.issw.acme.app.demorig.xulapp”
label=”Desktop Application (XULRunner)”
iconUrl=”icons/icon16.png”
platform=”win32″
programCommand=”C:\acme-app\app-xul\runApp.bat”
workingDirectory=”C:\acme-app\app-xul”>
</nativeProgramLaunchItem>
<urlLaunchItem
iconUrl=”icons/icon16.png”
url=”
http://localhost:8777/portal-demo/index.html”
id=”com.ibm.issw.acme.app.demorig.app.portaldemo”
label=”Portal Simulation”/>
</LauncherSet>
</extension>
</plugin>

This results in two items being added to the launcher menu on the Expeditor desktop, each with their own icon. One opens a browser window with the given URL, the other launches an application installed on the underlying operating system. Note that you can make native application launches relevant to the OS using the platform attribute — i.e. Windows applications will only be offered when platform is set to win32, for example.

Full details of the extension point can be found in the Info Center.

Posted in Snippets | Tagged: , , , | Leave a Comment »

Quick guide to using SSL/TLS support in MQTT and the micro broker

Posted by Martin on January 19, 2009

I have recently been using the new TLS/SSL support for the micro broker and MQTT v5 client introduced in Expeditor 6.2. I have recorded the following instructions as a quick-start guide to create a simple, server-authentication SSL connection using a self-signed certificate. Further information on configuring SSL for micro broker can be found on the Redbooks Wiki.

1. Create key pair and keystore for the micro broker

Find the binaries directory for the JRE you are using. For the Expeditor DRE (J2SE) runtime, for example, they can be found in a folder named similar to (may vary slightly with build levels)  C:\Program Files\IBM\Lotus\Expeditor\rcp\eclipse\plugins\com.ibm.rcp.j2se.win32.x86_1.6.0.20081029a-200811140851\jre\bin.

When in the JRE binaries directory, issue the following command to create a certificate:

keytool -genkey -alias sampleCert -keystore c:\sample\sampleStore.jks -keypass default -storepass default -dname “CN=Sample, OU=PVC, O=IBM, C=UK” -keyalg RSA

Make sure you are using the same JRE to create the keystore as you use to run the micro broker as the keystores aren’t compatible across different JREs.

2. Write the logic in a plugin to create an SSL listener on the broker

This is achieved using the micro broker administrative API. The following will add an SSL listener alongside the default non-encrypted listener.

public static final String BROKER_INSTANCE_NAME = “Sample_Broker”;
private static final int  BROKER_SECURE_PORT = 8883;
private static final String BROKER_KEYSTORE = “C:/sample/sampleStore.jks”;
// Password as per the keytool sample above
private static final char[] BROKER_KEYSTORE_PASSWORD ={‘d’,'e’,'f’,'a’,'u’,'l’,'t’};

BrokerDefinition def = brokerFactory.createBrokerDefinition(BROKER_INSTANCE_NAME);
def.setDataDirectory(Platform.getInstanceLocation().getURL().getFile()+”/microbroker”);
def.setAutoStartEnabled(false);
broker = brokerFactory.create(def);

// Now start up
broker.start();
// Now add SSL support — we want both SSL and non-SSL support
// otherwise we could have specified an SSL-only approach with
// def.setDefaultListenerSecure(true);
// and then set the SSL definition as per the following snippet.
// Note that the broker must be started to add the SSL listener
// if we want both secured and non-secured listeners. The broker
// can only be configured with a single listener initially.
Communications brokerComms = broker.getCommunications();

MQTTTCPListenerDefinition mqttld =
brokerComms.createMQTTTCPListenerDefinition(BROKER_SECURE_PORT);
mqttld.setSecure(true);
ServerSSLDefinition ssld = mqttld.createSSLDefinition();
ssld.setKeyStore(BROKER_KEYSTORE);
ssld.setKeyStorePassword(BROKER_KEYSTORE_PASSWORD);
mqttld.setSSLDefinition(ssld);
brokerComms.addTCPListener(mqttld);

Once your application has been started up (and hopefully your broker configured and running successfully) you
can check it is running by using a command such as (on Windows):

netstat -aN | find “LISTEN”

and see something listening on port 8883 (and 1883 for that matter if you use the above snippet).

3. Set the connection URI of the MQTT v5 client to use SSL

In the simple (server authentication) case, an MQTT v5 client can be connected using SSL simply by modifying the MQTT URI used at connection time from something like

tcp://localhost:1883

to

ssl://localhost:8883

The MQTT client will then know to attempt the connection using SSL.

A note on using self-signed certificates

By default, Expeditor running in GUI mode will offer a dialogue to the user prompting them as to whether they wish to accept the server certificate or reject it since the provider is not a recognised certification authority (i.e. it’s a certificate we’ve created ourselves for testing).

image

There are a couple of things to watch out for here. If the GUI is not yet available (i.e. the MQTT client application has loaded and attempted to connect before the GUI has loaded) or is not available (i.e. Expeditor is running “headless”) by default the Expeditor SSL support will automatically reject certificates from non-trusted certification authorities.

In order to prevent this issue, we must modify the plugin_customization.ini file
which by default is installed on Windows in

C:\Program Files\IBM\Lotus\Expeditor\rcp

Add the following line to always allow certificates from unknown certification authorities:

com.ibm.rcp.security.jceproxy/ssl.unknowncert.action=ALLOW

Note that an addition command line parameter is required in any Run Configuration profiles using the Expeditor test environment to point at the plugin_customization.ini file:

-plugincustomization “C:/Program Files/IBM/Lotus/Expeditor/rcp/plugin_customization.ini”

image

WARNING: this will cause Expeditor to always accepted untrusted certificates — this tip is intended for development purposes and should NOT be used in production deployments.

Full details of Expeditor’s SSL configuration options can be found on the Expeditor Info Center.

Posted in Snippets | Tagged: , , , , , , , , | 1 Comment »

Using the new MQTT v5 client in Expeditor 6.2

Posted by Martin on January 14, 2009

With the launch of Expeditor 6.2 comes the next generation of the MQTT client. MQTT is IBM’s specialist messaging protocol designed for use in fragile or expensive networks (e.g. mobile or satellite links) and in constrained devices such as sensors and mobile devices. This latest version (version 5 of the protocol) adds a number of features including the following:

  • Support for the point-to-point (i.e. queues) as well as the traditional pub/sub messaging paradigm.
  • Different payload types such as a textual string in addition to the basic byte array of the previous client.
  • The ability to specify whether an individual subscription is durable (i.e. survives a disconnection and continues to collect messages) or not on a per-subscription basis. In the previous incarnation, the durability of a client’s subscriptions was specified for the whole client. This could mean resources within the broker were consumed by unnecessarily durable subscriptions.
  • The ability to connect to a messaging server over SSL and using authentication credentials.
  • A variety of additional header information for messages including expiry and priority as in JMS.
  • The ability to start and stop message delivery without having to disconnect and reconnect again. This allows the application to control the flow of messages without the overhead of a full connection handshake each time.

Full details can be found in the Javadoc but I’ve included a simple sample here to get you started.

Install the MQTT v5 client into the Expeditor client runtime

In Expeditor 6.2, the MQTT v5 client is not installed by default with the base client installer. You will need to install it as an additional feature from the update site contained in desktop/updates/platform folder in the install media, even for use in the toolkit. A screen shot showing which feature is required is shown below.

image

Sample MQTT application

The following examples show a class that connects to a broker and subscribes durably for messages and a class that publishes a message. The subscriber makes use of the message delivery start/stop feature to enable message delivery only when both subscriptions are in place. You will see that I’ve included some tests that determine the type of payload of a given message as well.

Notice the different package/plug-in name for the v5 MQTT client.

import com.ibm.micro.client.MqttCallback;
import com.ibm.micro.client.MqttClient;
import com.ibm.micro.client.MqttConnectOptions;
import com.ibm.micro.client.MqttDeliveryToken;
import com.ibm.micro.client.MqttDestination;
import com.ibm.micro.client.MqttException;
import com.ibm.micro.client.MqttMessage;
import com.ibm.micro.client.MqttSubscriptionOptions;

public class MqttSubscriber implements MqttCallback {

private MqttClient mqttClient = null;
private String TOPIC_SUFFIX_ALERTS = “alerts”;
private String TOPIC_SUFFIX_DATA = “data”;
public MqttSubscriber() {
super();
}
/* MQTT Client API */
public void connectionLost(Throwable arg0) {
//
System.out.println(“Connection to the micro broker lost”);
}

public void deliveryComplete(MqttDeliveryToken arg0) {
//
}

public void deliveryFailed(MqttDeliveryToken arg0, MqttException arg1) {
//
}

public void messageArrived(MqttDestination destination, MqttMessage message) throws Exception {
System.out.println(“Message has arrived over MQTT.”);
String sourceTopic = destination.getName();
// In MQTT v5 we have different payload types.
if (message.getPayloadType() == MqttMessage.PAYLOAD_TEXT) {
String payload = message.getStringPayload();
System.out.println(“Message payload: “+payload);
} else {
// Enforce a “text only” policy.
System.err.println(“Message is not a text string.”);
}
}

public void start(String mqttUri, String name) throws MqttException {
mqttClient = new MqttClient(mqttUri, “Sub_Client”);
mqttClient.setCallback(this);
MqttConnectOptions options = new MqttConnectOptions();
// Wait until we’re ready to receive messages (overrides default)
options.setAutoStart(false);
// We don’t want our state cleaned up, we need to keep
// Durable subscriptions.
options.setPurge(false);
// Connect
mqttClient.connect(options);

MqttSubscriptionOptions subOpts = new MqttSubscriptionOptions();
subOpts.setDurable(true); // We want durable subscriptions
subOpts.setQos(2); // Once and once only delivery (same as V3 QoS).
mqttClient.subscribe(“acme/sample/”+name+”/”+TOPIC_SUFFIX_ALERTS+”/+”, subOpts);
mqttClient.subscribe(“acme/sample/”+name+”/”+TOPIC_SUFFIX_DATA+”/+”, subOpts);
// Ready to receive.
mqttClient.startListening();

System.out.println(“Connected to “+mqttUri);
}

public void stop() throws MqttException {
//
mqttClient.disconnect();
}

}

The following is the complimentary part of the sample that shows how to publish a message using the v5 client. This class is intended to be run as a simple command line utility.

import com.ibm.micro.client.MqttClient;
import com.ibm.micro.client.MqttException;
import com.ibm.micro.client.MqttMessage;
import com.ibm.micro.client.MqttTopic;

public class MqttPublisher {

public static void main(String[] args) {
try {
// Parameters: <uri> <clientid> <topic> <qos: 0,1 or 2> <string data>
MqttPublisher publisher = new MqttPublisher(args[0], args[1]);
publisher.publish(args[2], Integer.parseInt(args[3]), args[4]);
} catch (Exception ex) {
ex.printStackTrace();
}
}
private String uri = null;
private String clientId = null;
public MqttPublisher(String u, String c) {
uri = u;
clientId = c;
}

public void publish(String topic, int qos, String payload) throws MqttException {
MqttClient client = new MqttClient(“tcp://localhost:1883″, “Pub_Client”);
client.connect();
// Notice the new object model for destinations and messages
MqttTopic t = client.getTopic(topic);
// Create a string payload — v5 discriminates between payload types unlike v3.
MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
t.publish(message);
client.disconnect();
}
}

Enjoy !

Posted in Snippets | Tagged: , , , , , , , , , , , | Leave a Comment »