diff --git a/assembly.xml b/assembly.xml
index 031ae05..7d55db0 100644
--- a/assembly.xml
+++ b/assembly.xml
@@ -20,6 +20,8 @@
lib
mqtt-client-0.4.0.jar
+ bcprov-jdk15on-1.48.jar
+ bcpkix-jdk15on-1.48.jar
diff --git a/doc/example.png b/doc/example.png
index a7e9496..d935cb8 100644
Binary files a/doc/example.png and b/doc/example.png differ
diff --git a/pom.xml b/pom.xml
index f19c4b2..68cd173 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,6 +63,16 @@
mqtt-client
0.4.0
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.48
+
+
+ org.bouncycastle
+ bcpkix-jdk15on
+ 1.48
+
diff --git a/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerDialog.java b/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerDialog.java
index 37d3888..a87a992 100644
--- a/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerDialog.java
+++ b/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerDialog.java
@@ -2,6 +2,8 @@
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
@@ -12,6 +14,7 @@
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
@@ -20,6 +23,7 @@
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.pentaho.di.core.Const;
+import org.pentaho.di.core.Props;
import org.pentaho.di.core.exception.KettleStepException;
import org.pentaho.di.core.row.RowMeta;
import org.pentaho.di.core.row.RowMetaInterface;
@@ -40,14 +44,33 @@ public class MQTTProducerDialog extends BaseStepDialog implements
StepDialogInterface {
private MQTTProducerMeta producerMeta;
+
+ private CCombo wInputField;
+
+ private CTabFolder wTabFolder;
+
+ private CTabItem wGeneralTab;
private TextVar wBroker;
private TextVar wTopicName;
private TextVar wClientID;
private TextVar wTimeout;
private TextVar wQOS;
- private CCombo wInputField;
- public MQTTProducerDialog(Shell parent, Object in, TransMeta tr, String sname) {
+ private CTabItem wCredentialsTab;
+ private Button wRequiresAuth;
+ private Label wlUsername;
+ private TextVar wUsername;
+ private Label wlPassword;
+ private TextVar wPassword;
+
+ private CTabItem wSSLTab;
+ private TextVar wCAFile;
+ private TextVar wCertFile;
+ private TextVar wKeyFile;
+ private TextVar wKeyPassword;
+
+ public MQTTProducerDialog(Shell parent, Object in, TransMeta tr,
+ String sname) {
super(parent, (BaseStepMeta) in, tr, sname);
producerMeta = (MQTTProducerMeta) in;
}
@@ -58,7 +81,8 @@ public MQTTProducerDialog(Shell parent, BaseStepMeta baseStepMeta,
producerMeta = (MQTTProducerMeta) baseStepMeta;
}
- public MQTTProducerDialog(Shell parent, int nr, BaseStepMeta in, TransMeta tr) {
+ public MQTTProducerDialog(Shell parent, int nr, BaseStepMeta in,
+ TransMeta tr) {
super(parent, nr, in, tr);
producerMeta = (MQTTProducerMeta) in;
}
@@ -109,28 +133,84 @@ public void modifyText(ModifyEvent e) {
wStepname.setLayoutData(fdStepname);
Control lastControl = wStepname;
+ // Input field
+ RowMetaInterface previousFields;
+ try {
+ previousFields = transMeta.getPrevStepFields(stepMeta);
+ } catch (KettleStepException e) {
+ new ErrorDialog(
+ shell,
+ BaseMessages.getString("System.Dialog.Error.Title"),
+ Messages.getString("MQTTClientDialog.ErrorDialog.UnableToGetInputFields.Message"),
+ e);
+ previousFields = new RowMeta();
+ }
+ Label wlInputField = new Label(shell, SWT.RIGHT);
+ wlInputField.setText(Messages
+ .getString("MQTTClientDialog.FieldName.Label"));
+ props.setLook(wlInputField);
+ FormData fdlInputField = new FormData();
+ fdlInputField.top = new FormAttachment(lastControl, margin);
+ fdlInputField.left = new FormAttachment(0, 0);
+ fdlInputField.right = new FormAttachment(middle, -margin);
+ wlInputField.setLayoutData(fdlInputField);
+ wInputField = new CCombo(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ wInputField.setToolTipText(Messages
+ .getString("MQTTClientDialog.FieldName.Tooltip"));
+ wInputField.setItems(previousFields.getFieldNames());
+ props.setLook(wInputField);
+ wInputField.addModifyListener(lsMod);
+ FormData fdFilename = new FormData();
+ fdFilename.top = new FormAttachment(lastControl, margin);
+ fdFilename.left = new FormAttachment(middle, 0);
+ fdFilename.right = new FormAttachment(100, 0);
+ wInputField.setLayoutData(fdFilename);
+ lastControl = wInputField;
+
+ // ====================
+ // START OF TAB FOLDER
+ // ====================
+ wTabFolder = new CTabFolder(shell, SWT.BORDER);
+ props.setLook(wTabFolder, Props.WIDGET_STYLE_TAB);
+
+ // ====================
+ // GENERAL TAB
+ // ====================
+
+ wGeneralTab = new CTabItem(wTabFolder, SWT.NONE);
+ wGeneralTab.setText(Messages
+ .getString("MQTTClientDialog.GeneralTab.Label")); //$NON-NLS-1$
+
+ FormLayout mainLayout = new FormLayout();
+ mainLayout.marginWidth = 3;
+ mainLayout.marginHeight = 3;
+
+ Composite wGeneralTabComp = new Composite(wTabFolder, SWT.NONE);
+ props.setLook(wGeneralTabComp);
+ wGeneralTabComp.setLayout(mainLayout);
+
// Broker URL
- Label wlBroker = new Label(shell, SWT.RIGHT);
+ Label wlBroker = new Label(wGeneralTabComp, SWT.RIGHT);
wlBroker.setText(Messages.getString("MQTTClientDialog.Broker.Label"));
props.setLook(wlBroker);
FormData fdlBroker = new FormData();
- fdlBroker.top = new FormAttachment(lastControl, margin);
+ fdlBroker.top = new FormAttachment(0, margin * 2);
fdlBroker.left = new FormAttachment(0, 0);
fdlBroker.right = new FormAttachment(middle, -margin);
wlBroker.setLayoutData(fdlBroker);
- wBroker = new TextVar(transMeta, shell, SWT.SINGLE | SWT.LEFT
+ wBroker = new TextVar(transMeta, wGeneralTabComp, SWT.SINGLE | SWT.LEFT
| SWT.BORDER);
props.setLook(wBroker);
wBroker.addModifyListener(lsMod);
FormData fdBroker = new FormData();
- fdBroker.top = new FormAttachment(lastControl, margin);
+ fdBroker.top = new FormAttachment(0, margin * 2);
fdBroker.left = new FormAttachment(middle, 0);
fdBroker.right = new FormAttachment(100, 0);
wBroker.setLayoutData(fdBroker);
lastControl = wBroker;
// Topic name
- Label wlTopicName = new Label(shell, SWT.RIGHT);
+ Label wlTopicName = new Label(wGeneralTabComp, SWT.RIGHT);
wlTopicName.setText(Messages
.getString("MQTTClientDialog.TopicName.Label"));
props.setLook(wlTopicName);
@@ -139,8 +219,8 @@ public void modifyText(ModifyEvent e) {
fdlTopicName.left = new FormAttachment(0, 0);
fdlTopicName.right = new FormAttachment(middle, -margin);
wlTopicName.setLayoutData(fdlTopicName);
- wTopicName = new TextVar(transMeta, shell, SWT.SINGLE | SWT.LEFT
- | SWT.BORDER);
+ wTopicName = new TextVar(transMeta, wGeneralTabComp, SWT.SINGLE
+ | SWT.LEFT | SWT.BORDER);
props.setLook(wTopicName);
wTopicName.addModifyListener(lsMod);
FormData fdTopicName = new FormData();
@@ -151,7 +231,7 @@ public void modifyText(ModifyEvent e) {
lastControl = wTopicName;
// Client ID
- Label wlClientID = new Label(shell, SWT.RIGHT);
+ Label wlClientID = new Label(wGeneralTabComp, SWT.RIGHT);
wlClientID.setText(Messages
.getString("MQTTClientDialog.ClientID.Label"));
props.setLook(wlClientID);
@@ -160,8 +240,8 @@ public void modifyText(ModifyEvent e) {
fdlClientID.left = new FormAttachment(0, 0);
fdlClientID.right = new FormAttachment(middle, -margin);
wlClientID.setLayoutData(fdlClientID);
- wClientID = new TextVar(transMeta, shell, SWT.SINGLE | SWT.LEFT
- | SWT.BORDER);
+ wClientID = new TextVar(transMeta, wGeneralTabComp, SWT.SINGLE
+ | SWT.LEFT | SWT.BORDER);
props.setLook(wClientID);
wClientID.addModifyListener(lsMod);
FormData fdClientID = new FormData();
@@ -172,7 +252,7 @@ public void modifyText(ModifyEvent e) {
lastControl = wClientID;
// Connection timeout
- Label wlConnectionTimeout = new Label(shell, SWT.RIGHT);
+ Label wlConnectionTimeout = new Label(wGeneralTabComp, SWT.RIGHT);
wlConnectionTimeout.setText(Messages
.getString("MQTTClientDialog.ConnectionTimeout.Label"));
props.setLook(wlConnectionTimeout);
@@ -181,8 +261,8 @@ public void modifyText(ModifyEvent e) {
fdlConnectionTimeout.left = new FormAttachment(0, 0);
fdlConnectionTimeout.right = new FormAttachment(middle, -margin);
wlConnectionTimeout.setLayoutData(fdlConnectionTimeout);
- wTimeout = new TextVar(transMeta, shell, SWT.SINGLE | SWT.LEFT
- | SWT.BORDER);
+ wTimeout = new TextVar(transMeta, wGeneralTabComp, SWT.SINGLE
+ | SWT.LEFT | SWT.BORDER);
props.setLook(wTimeout);
wTimeout.addModifyListener(lsMod);
FormData fdConnectionTimeout = new FormData();
@@ -193,7 +273,7 @@ public void modifyText(ModifyEvent e) {
lastControl = wTimeout;
// QOS
- Label wlQOS = new Label(shell, SWT.RIGHT);
+ Label wlQOS = new Label(wGeneralTabComp, SWT.RIGHT);
wlQOS.setText(Messages.getString("MQTTClientDialog.QOS.Label"));
props.setLook(wlQOS);
FormData fdlQOS = new FormData();
@@ -201,7 +281,8 @@ public void modifyText(ModifyEvent e) {
fdlQOS.left = new FormAttachment(0, 0);
fdlQOS.right = new FormAttachment(middle, -margin);
wlQOS.setLayoutData(fdlQOS);
- wQOS = new TextVar(transMeta, shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ wQOS = new TextVar(transMeta, wGeneralTabComp, SWT.SINGLE | SWT.LEFT
+ | SWT.BORDER);
props.setLook(wQOS);
wQOS.addModifyListener(lsMod);
FormData fdQOS = new FormData();
@@ -211,39 +292,241 @@ public void modifyText(ModifyEvent e) {
wQOS.setLayoutData(fdQOS);
lastControl = wQOS;
- // Input field
- RowMetaInterface previousFields;
- try {
- previousFields = transMeta.getPrevStepFields(stepMeta);
- } catch (KettleStepException e) {
- new ErrorDialog(
- shell,
- BaseMessages.getString("System.Dialog.Error.Title"),
- Messages.getString("MQTTClientDialog.ErrorDialog.UnableToGetInputFields.Message"),
- e);
- previousFields = new RowMeta();
- }
- Label wlInputField = new Label(shell, SWT.RIGHT);
- wlInputField.setText(Messages
- .getString("MQTTClientDialog.FieldName.Label"));
- props.setLook(wlInputField);
- FormData fdlInputField = new FormData();
- fdlInputField.top = new FormAttachment(lastControl, margin);
- fdlInputField.left = new FormAttachment(0, 0);
- fdlInputField.right = new FormAttachment(middle, -margin);
- wlInputField.setLayoutData(fdlInputField);
- wInputField = new CCombo(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
- wInputField.setItems(previousFields.getFieldNames());
- props.setLook(wInputField);
- wInputField.addModifyListener(lsMod);
- FormData fdFilename = new FormData();
- fdFilename.top = new FormAttachment(lastControl, margin);
- fdFilename.left = new FormAttachment(middle, 0);
- fdFilename.right = new FormAttachment(100, 0);
- wInputField.setLayoutData(fdFilename);
- lastControl = wInputField;
+ FormData fdGeneralTabComp = new FormData();
+ fdGeneralTabComp.left = new FormAttachment(0, 0);
+ fdGeneralTabComp.top = new FormAttachment(0, 0);
+ fdGeneralTabComp.right = new FormAttachment(100, 0);
+ fdGeneralTabComp.bottom = new FormAttachment(100, 0);
+ wGeneralTabComp.setLayoutData(fdGeneralTabComp);
+
+ wGeneralTabComp.layout();
+ wGeneralTab.setControl(wGeneralTabComp);
+
+ // ====================
+ // CREDENTIALS TAB
+ // ====================
+ wCredentialsTab = new CTabItem(wTabFolder, SWT.NONE);
+ wCredentialsTab.setText(Messages
+ .getString("MQTTClientDialog.CredentialsTab.Title")); //$NON-NLS-1$
+
+ Composite wCredentialsComp = new Composite(wTabFolder, SWT.NONE);
+ props.setLook(wCredentialsComp);
+
+ FormLayout fieldsCompLayout = new FormLayout();
+ fieldsCompLayout.marginWidth = Const.FORM_MARGIN;
+ fieldsCompLayout.marginHeight = Const.FORM_MARGIN;
+ wCredentialsComp.setLayout(fieldsCompLayout);
+
+ Label wlRequiresAuth = new Label(wCredentialsComp, SWT.RIGHT);
+ wlRequiresAuth.setText(Messages
+ .getString("MQTTClientDialog.RequireAuth.Label"));
+ props.setLook(wlRequiresAuth);
+ FormData fdlRequriesAuth = new FormData();
+ fdlRequriesAuth.left = new FormAttachment(0, 0);
+ fdlRequriesAuth.top = new FormAttachment(0, margin * 2);
+ fdlRequriesAuth.right = new FormAttachment(middle, -margin);
+ wlRequiresAuth.setLayoutData(fdlRequriesAuth);
+ wRequiresAuth = new Button(wCredentialsComp, SWT.CHECK);
+ props.setLook(wRequiresAuth);
+ FormData fdRequiresAuth = new FormData();
+ fdRequiresAuth.left = new FormAttachment(middle, 0);
+ fdRequiresAuth.top = new FormAttachment(0, margin * 2);
+ fdRequiresAuth.right = new FormAttachment(100, 0);
+ wRequiresAuth.setLayoutData(fdRequiresAuth);
+
+ wRequiresAuth.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent arg0) {
+ boolean enabled = wRequiresAuth.getSelection();
+ wlUsername.setEnabled(enabled);
+ wUsername.setEnabled(enabled);
+ wlPassword.setEnabled(enabled);
+ wPassword.setEnabled(enabled);
+ }
+ });
+ lastControl = wRequiresAuth;
+
+ // Username field
+ wlUsername = new Label(wCredentialsComp, SWT.RIGHT);
+ wlUsername.setEnabled(false);
+ wlUsername.setText(Messages
+ .getString("MQTTClientDialog.Username.Label")); //$NON-NLS-1$
+ props.setLook(wlUsername);
+ FormData fdlUsername = new FormData();
+ fdlUsername.left = new FormAttachment(0, -margin);
+ fdlUsername.right = new FormAttachment(middle, -2 * margin);
+ fdlUsername.top = new FormAttachment(lastControl, 2 * margin);
+ wlUsername.setLayoutData(fdlUsername);
+
+ wUsername = new TextVar(transMeta, wCredentialsComp, SWT.SINGLE
+ | SWT.LEFT | SWT.BORDER);
+ wUsername.setEnabled(false);
+ wUsername.setToolTipText(Messages
+ .getString("MQTTClientDialog.Username.Tooltip"));
+ props.setLook(wUsername);
+ wUsername.addModifyListener(lsMod);
+ FormData fdResult = new FormData();
+ fdResult.left = new FormAttachment(middle, -margin);
+ fdResult.top = new FormAttachment(lastControl, 2 * margin);
+ fdResult.right = new FormAttachment(100, 0);
+ wUsername.setLayoutData(fdResult);
+ lastControl = wUsername;
+
+ // Password field
+ wlPassword = new Label(wCredentialsComp, SWT.RIGHT);
+ wlPassword.setEnabled(false);
+ wlPassword.setText(Messages
+ .getString("MQTTClientDialog.Password.Label")); //$NON-NLS-1$
+ props.setLook(wlPassword);
+ FormData fdlPassword = new FormData();
+ fdlPassword.left = new FormAttachment(0, -margin);
+ fdlPassword.right = new FormAttachment(middle, -2 * margin);
+ fdlPassword.top = new FormAttachment(lastControl, margin);
+ wlPassword.setLayoutData(fdlPassword);
- // Buttons
+ wPassword = new TextVar(transMeta, wCredentialsComp, SWT.SINGLE
+ | SWT.LEFT | SWT.BORDER | SWT.PASSWORD);
+ wPassword.setEnabled(false);
+ wPassword.setToolTipText(Messages
+ .getString("MQTTClientDialog.Password.Tooltip"));
+ props.setLook(wPassword);
+ wPassword.addModifyListener(lsMod);
+ FormData fdPassword = new FormData();
+ fdPassword.left = new FormAttachment(middle, -margin);
+ fdPassword.top = new FormAttachment(lastControl, margin);
+ fdPassword.right = new FormAttachment(100, 0);
+ wPassword.setLayoutData(fdPassword);
+
+ FormData fdCredentialsComp = new FormData();
+ fdCredentialsComp.left = new FormAttachment(0, 0);
+ fdCredentialsComp.top = new FormAttachment(0, 0);
+ fdCredentialsComp.right = new FormAttachment(100, 0);
+ fdCredentialsComp.bottom = new FormAttachment(100, 0);
+ wCredentialsComp.setLayoutData(fdCredentialsComp);
+
+ wCredentialsComp.layout();
+ wCredentialsTab.setControl(wCredentialsComp);
+
+ // ====================
+ // SSL TAB
+ // ====================
+ wSSLTab = new CTabItem(wTabFolder, SWT.NONE);
+ wSSLTab.setText(Messages.getString("MQTTClientDialog.SSLTab.Label")); //$NON-NLS-1$
+
+ Composite wSSLComp = new Composite(wTabFolder, SWT.NONE);
+ props.setLook(wSSLComp);
+
+ FormLayout sslCompLayout = new FormLayout();
+ sslCompLayout.marginWidth = Const.FORM_MARGIN;
+ sslCompLayout.marginHeight = Const.FORM_MARGIN;
+ wSSLComp.setLayout(sslCompLayout);
+
+ // Server CA file path
+ Label wlCAFile = new Label(wSSLComp, SWT.RIGHT);
+ wlCAFile.setText(Messages.getString("MQTTClientDialog.CAFile.Label")); //$NON-NLS-1$
+ props.setLook(wlCAFile);
+ FormData fdlCAFile = new FormData();
+ fdlCAFile.left = new FormAttachment(0, -margin);
+ fdlCAFile.right = new FormAttachment(middle, -2 * margin);
+ fdlCAFile.top = new FormAttachment(0, 2 * margin);
+ wlCAFile.setLayoutData(fdlCAFile);
+
+ wCAFile = new TextVar(transMeta, wSSLComp, SWT.SINGLE | SWT.LEFT
+ | SWT.BORDER);
+ wCAFile.setToolTipText(Messages
+ .getString("MQTTClientDialog.CAFile.Tooltip"));
+ props.setLook(wCAFile);
+ wCAFile.addModifyListener(lsMod);
+ FormData fdCAFile = new FormData();
+ fdCAFile.left = new FormAttachment(middle, -margin);
+ fdCAFile.top = new FormAttachment(0, 2 * margin);
+ fdCAFile.right = new FormAttachment(100, 0);
+ wCAFile.setLayoutData(fdCAFile);
+ lastControl = wCAFile;
+
+ // Client certificate file path
+ Label wlCertFile = new Label(wSSLComp, SWT.RIGHT);
+ wlCertFile.setText(Messages
+ .getString("MQTTClientDialog.CertFile.Label")); //$NON-NLS-1$
+ props.setLook(wlCertFile);
+ FormData fdlCertFile = new FormData();
+ fdlCertFile.left = new FormAttachment(0, -margin);
+ fdlCertFile.right = new FormAttachment(middle, -2 * margin);
+ fdlCertFile.top = new FormAttachment(lastControl, margin);
+ wlCertFile.setLayoutData(fdlCertFile);
+
+ wCertFile = new TextVar(transMeta, wSSLComp, SWT.SINGLE | SWT.LEFT
+ | SWT.BORDER);
+ wCertFile.setToolTipText(Messages
+ .getString("MQTTClientDialog.CertFile.Tooltip"));
+ props.setLook(wCertFile);
+ wCertFile.addModifyListener(lsMod);
+ FormData fdCertFile = new FormData();
+ fdCertFile.left = new FormAttachment(middle, -margin);
+ fdCertFile.top = new FormAttachment(lastControl, margin);
+ fdCertFile.right = new FormAttachment(100, 0);
+ wCertFile.setLayoutData(fdCertFile);
+ lastControl = wCertFile;
+
+ // Client key file path
+ Label wlKeyFile = new Label(wSSLComp, SWT.RIGHT);
+ wlKeyFile.setText(Messages.getString("MQTTClientDialog.KeyFile.Label")); //$NON-NLS-1$
+ props.setLook(wlKeyFile);
+ FormData fdlKeyFile = new FormData();
+ fdlKeyFile.left = new FormAttachment(0, -margin);
+ fdlKeyFile.right = new FormAttachment(middle, -2 * margin);
+ fdlKeyFile.top = new FormAttachment(lastControl, margin);
+ wlKeyFile.setLayoutData(fdlKeyFile);
+
+ wKeyFile = new TextVar(transMeta, wSSLComp, SWT.SINGLE | SWT.LEFT
+ | SWT.BORDER);
+ wKeyFile.setToolTipText(Messages
+ .getString("MQTTClientDialog.KeyFile.Tooltip"));
+ props.setLook(wKeyFile);
+ wKeyFile.addModifyListener(lsMod);
+ FormData fdKeyFile = new FormData();
+ fdKeyFile.left = new FormAttachment(middle, -margin);
+ fdKeyFile.top = new FormAttachment(lastControl, margin);
+ fdKeyFile.right = new FormAttachment(100, 0);
+ wKeyFile.setLayoutData(fdKeyFile);
+ lastControl = wKeyFile;
+
+ // Client key file password path
+ Label wlKeyPassword = new Label(wSSLComp, SWT.RIGHT);
+ wlKeyPassword.setText(Messages
+ .getString("MQTTClientDialog.KeyPassword.Label")); //$NON-NLS-1$
+ props.setLook(wlKeyPassword);
+ FormData fdlKeyPassword = new FormData();
+ fdlKeyPassword.left = new FormAttachment(0, -margin);
+ fdlKeyPassword.right = new FormAttachment(middle, -2 * margin);
+ fdlKeyPassword.top = new FormAttachment(lastControl, margin);
+ wlKeyPassword.setLayoutData(fdlKeyPassword);
+
+ wKeyPassword = new TextVar(transMeta, wSSLComp, SWT.SINGLE | SWT.LEFT
+ | SWT.BORDER | SWT.PASSWORD);
+ wKeyPassword.setToolTipText(Messages
+ .getString("MQTTClientDialog.KeyPassword.Tooltip"));
+ props.setLook(wKeyPassword);
+ wKeyPassword.addModifyListener(lsMod);
+ FormData fdKeyPassword = new FormData();
+ fdKeyPassword.left = new FormAttachment(middle, -margin);
+ fdKeyPassword.top = new FormAttachment(lastControl, margin);
+ fdKeyPassword.right = new FormAttachment(100, 0);
+ wKeyPassword.setLayoutData(fdKeyPassword);
+ lastControl = wKeyPassword;
+
+ FormData fdSSLComp = new FormData();
+ fdSSLComp.left = new FormAttachment(0, 0);
+ fdSSLComp.top = new FormAttachment(0, 0);
+ fdSSLComp.right = new FormAttachment(100, 0);
+ fdSSLComp.bottom = new FormAttachment(100, 0);
+ wSSLComp.setLayoutData(fdSSLComp);
+
+ wSSLComp.layout();
+ wSSLTab.setControl(wSSLComp);
+
+ // ====================
+ // BUTTONS
+ // ====================
wOK = new Button(shell, SWT.PUSH);
wOK.setText(BaseMessages.getString("System.Button.OK")); //$NON-NLS-1$
wCancel = new Button(shell, SWT.PUSH);
@@ -251,6 +534,16 @@ public void modifyText(ModifyEvent e) {
setButtonPositions(new Button[] { wOK, wCancel }, margin, null);
+ // ====================
+ // END OF TAB FOLDER
+ // ====================
+ FormData fdTabFolder = new FormData();
+ fdTabFolder.left = new FormAttachment(0, 0);
+ fdTabFolder.top = new FormAttachment(wInputField, margin);
+ fdTabFolder.right = new FormAttachment(100, 0);
+ fdTabFolder.bottom = new FormAttachment(wOK, -margin);
+ wTabFolder.setLayoutData(fdTabFolder);
+
// Add listeners
lsCancel = new Listener() {
public void handleEvent(Event e) {
@@ -274,6 +567,8 @@ public void widgetDefaultSelected(SelectionEvent e) {
wTopicName.addSelectionListener(lsDef);
wInputField.addSelectionListener(lsDef);
+ wTabFolder.setSelection(0);
+
// Detect X or ALT-F4 or something that kills this window...
shell.addShellListener(new ShellAdapter() {
public void shellClosed(ShellEvent e) {
@@ -309,6 +604,18 @@ private void getData(MQTTProducerMeta producerMeta, boolean copyStepname) {
wClientID.setText(Const.NVL(producerMeta.getClientId(), ""));
wTimeout.setText(Const.NVL(producerMeta.getTimeout(), "10000"));
wQOS.setText(Const.NVL(producerMeta.getQoS(), "0"));
+
+ wRequiresAuth.setSelection(producerMeta.isRequiresAuth());
+ wRequiresAuth.notifyListeners(SWT.Selection, new Event());
+
+ wUsername.setText(Const.NVL(producerMeta.getUsername(), ""));
+ wPassword.setText(Const.NVL(producerMeta.getPassword(), ""));
+
+ wCAFile.setText(Const.NVL(producerMeta.getSSLCaFile(), ""));
+ wCertFile.setText(Const.NVL(producerMeta.getSSLCertFile(), ""));
+ wKeyFile.setText(Const.NVL(producerMeta.getSSLKeyFile(), ""));
+ wKeyPassword.setText(Const.NVL(producerMeta.getSSLKeyFilePass(), ""));
+
wStepname.selectAll();
}
@@ -328,6 +635,19 @@ private void setData(MQTTProducerMeta producerMeta) {
producerMeta.setClientId(wClientID.getText());
producerMeta.setTimeout(wTimeout.getText());
producerMeta.setQoS(wQOS.getText());
+
+ boolean requiresAuth = wRequiresAuth.getSelection();
+ producerMeta.setRequiresAuth(requiresAuth);
+ if (requiresAuth) {
+ producerMeta.setUsername(wUsername.getText());
+ producerMeta.setPassword(wPassword.getText());
+ }
+
+ producerMeta.setSSLCaFile(wCAFile.getText());
+ producerMeta.setSSLCertFile(wCertFile.getText());
+ producerMeta.setSSLKeyFile(wKeyFile.getText());
+ producerMeta.setSSLKeyFilePass(wKeyPassword.getText());
+
producerMeta.setChanged();
}
diff --git a/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerMeta.java b/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerMeta.java
index e5ea289..1969cf1 100644
--- a/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerMeta.java
+++ b/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerMeta.java
@@ -5,6 +5,7 @@
import org.pentaho.di.core.CheckResult;
import org.pentaho.di.core.CheckResultInterface;
+import org.pentaho.di.core.Const;
import org.pentaho.di.core.Counter;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.exception.KettleException;
@@ -36,6 +37,13 @@ public class MQTTProducerMeta extends BaseStepMeta implements StepMetaInterface
private String clientId;
private String timeout = "10000";
private String qos = "0";
+ private boolean requiresAuth;
+ private String username;
+ private String password;
+ private String sslCaFile;
+ private String sslCertFile;
+ private String sslKeyFile;
+ private String sslKeyFilePass;
/**
* @return Broker URL
@@ -127,6 +135,111 @@ public void setQoS(String qos) {
this.qos = qos;
}
+ /**
+ * @return Whether MQTT broker requires authentication
+ */
+ public boolean isRequiresAuth() {
+ return requiresAuth;
+ }
+
+ /**
+ * @param requiresAuth
+ * Whether MQTT broker requires authentication
+ */
+ public void setRequiresAuth(boolean requiresAuth) {
+ this.requiresAuth = requiresAuth;
+ }
+
+ /**
+ * @return Username to MQTT broker
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * @param username
+ * Username to MQTT broker
+ */
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ /**
+ * @return Password to MQTT broker
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * @param password
+ * Password to MQTT broker
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ /**
+ * @return Server CA file
+ */
+ public String getSSLCaFile() {
+ return sslCaFile;
+ }
+
+ /**
+ * @param sslCaFile
+ * Server CA file
+ */
+ public void setSSLCaFile(String sslCaFile) {
+ this.sslCaFile = sslCaFile;
+ }
+
+ /**
+ * @return Client certificate file
+ */
+ public String getSSLCertFile() {
+ return sslCertFile;
+ }
+
+ /**
+ * @param sslCertFile
+ * Client certificate file
+ */
+ public void setSSLCertFile(String sslCertFile) {
+ this.sslCertFile = sslCertFile;
+ }
+
+ /**
+ * @return Client key file
+ */
+ public String getSSLKeyFile() {
+ return sslKeyFile;
+ }
+
+ /**
+ * @param sslKeyFile
+ * Client key file
+ */
+ public void setSSLKeyFile(String sslKeyFile) {
+ this.sslKeyFile = sslKeyFile;
+ }
+
+ /**
+ * @return Client key file password
+ */
+ public String getSSLKeyFilePass() {
+ return sslKeyFilePass;
+ }
+
+ /**
+ * @param sslKeyFilePass
+ * Client key file password
+ */
+ public void setSSLKeyFilePass(String sslKeyFilePass) {
+ this.sslKeyFilePass = sslKeyFilePass;
+ }
+
public void check(List remarks, TransMeta transMeta,
StepMeta stepMeta, RowMetaInterface prev, String input[],
String output[], RowMetaInterface info) {
@@ -162,13 +275,27 @@ public void check(List remarks, TransMeta transMeta,
Messages.getString("MQTTClientMeta.Check.InvalidQOS"),
stepMeta));
}
+ if (requiresAuth) {
+ if (username == null) {
+ remarks.add(new CheckResult(
+ CheckResultInterface.TYPE_RESULT_ERROR,
+ Messages.getString("MQTTClientMeta.Check.InvalidUsername"),
+ stepMeta));
+ }
+ if (password == null) {
+ remarks.add(new CheckResult(
+ CheckResultInterface.TYPE_RESULT_ERROR,
+ Messages.getString("MQTTClientMeta.Check.InvalidPassword"),
+ stepMeta));
+ }
+ }
}
public StepInterface getStep(StepMeta stepMeta,
StepDataInterface stepDataInterface, int cnr, TransMeta transMeta,
Trans trans) {
- return new MQTTProducerStep(stepMeta, stepDataInterface, cnr, transMeta,
- trans);
+ return new MQTTProducerStep(stepMeta, stepDataInterface, cnr,
+ transMeta, trans);
}
public StepDataInterface getStepData() {
@@ -185,6 +312,20 @@ public void loadXML(Node stepnode, List databases,
clientId = XMLHandler.getTagValue(stepnode, "CLIENT_ID");
timeout = XMLHandler.getTagValue(stepnode, "TIMEOUT");
qos = XMLHandler.getTagValue(stepnode, "QOS");
+ requiresAuth = Boolean.parseBoolean(XMLHandler.getTagValue(
+ stepnode, "REQUIRES_AUTH"));
+ if (requiresAuth) {
+ username = XMLHandler.getTagValue(stepnode, "USERNAME");
+ password = XMLHandler.getTagValue(stepnode, "PASSWORD");
+ }
+ Node sslNode = XMLHandler.getSubNode(stepnode, "SSL");
+ if (sslNode != null) {
+ sslCaFile = XMLHandler.getTagValue(sslNode, "CA_FILE");
+ sslCertFile = XMLHandler.getTagValue(sslNode, "CERT_FILE");
+ sslKeyFile = XMLHandler.getTagValue(sslNode, "KEY_FILE");
+ sslKeyFilePass = XMLHandler.getTagValue(sslNode,
+ "KEY_FILE_PASS");
+ }
} catch (Exception e) {
throw new KettleXMLException(
Messages.getString("MQTTClientMeta.Exception.loadXml"), e);
@@ -216,6 +357,46 @@ public String getXML() throws KettleException {
if (qos != null) {
retval.append(" ").append(XMLHandler.addTagValue("QOS", qos));
}
+
+ retval.append(" ").append(
+ XMLHandler.addTagValue("REQUIRES_AUTH",
+ Boolean.toString(requiresAuth)));
+ if (requiresAuth) {
+ if (username != null) {
+ retval.append(" ").append(
+ XMLHandler.addTagValue("USERNAME", username));
+ }
+ if (password != null) {
+ retval.append(" ").append(
+ XMLHandler.addTagValue("PASSWORD", password));
+ }
+ }
+
+ if (sslCaFile != null || sslCertFile != null || sslKeyFile != null
+ || sslKeyFilePass != null) {
+ retval.append(" ").append(XMLHandler.openTag("SSL"))
+ .append(Const.CR);
+ if (sslCaFile != null) {
+ retval.append(" "
+ + XMLHandler.addTagValue("CA_FILE", sslCaFile));
+ }
+ if (sslCertFile != null) {
+ retval.append(" "
+ + XMLHandler.addTagValue("CERT_FILE", sslCertFile));
+ }
+ if (sslKeyFile != null) {
+ retval.append(" "
+ + XMLHandler.addTagValue("KEY_FILE", sslKeyFile));
+ }
+ if (sslKeyFilePass != null) {
+ retval.append(" "
+ + XMLHandler.addTagValue("KEY_FILE_PASS",
+ sslKeyFilePass));
+ }
+ retval.append(" ").append(XMLHandler.closeTag("SSL"))
+ .append(Const.CR);
+ }
+
return retval.toString();
}
@@ -229,6 +410,17 @@ public void readRep(Repository rep, ObjectId stepId,
clientId = rep.getStepAttributeString(stepId, "CLIENT_ID");
timeout = rep.getStepAttributeString(stepId, "TIMEOUT");
qos = rep.getStepAttributeString(stepId, "QOS");
+ requiresAuth = Boolean.parseBoolean(rep.getStepAttributeString(
+ stepId, "REQUIRES_AUTH"));
+ if (requiresAuth) {
+ username = rep.getStepAttributeString(stepId, "USERNAME");
+ password = rep.getStepAttributeString(stepId, "PASSWORD");
+ }
+ sslCaFile = rep.getStepAttributeString(stepId, "SSL_CA_FILE");
+ sslCertFile = rep.getStepAttributeString(stepId, "SSL_CERT_FILE");
+ sslKeyFile = rep.getStepAttributeString(stepId, "SSL_KEY_FILE");
+ sslKeyFilePass = rep.getStepAttributeString(stepId,
+ "SSL_KEY_FILE_PASS");
} catch (Exception e) {
throw new KettleException("MQTTClientMeta.Exception.loadRep", e);
}
@@ -258,6 +450,35 @@ public void saveRep(Repository rep, ObjectId transformationId,
if (qos != null) {
rep.saveStepAttribute(transformationId, stepId, "QOS", qos);
}
+ rep.saveStepAttribute(transformationId, stepId, "REQUIRES_AUTH",
+ Boolean.toString(requiresAuth));
+ if (requiresAuth) {
+ if (username != null) {
+ rep.saveStepAttribute(transformationId, stepId, "USERNAME",
+ username);
+ }
+ if (password != null) {
+ rep.saveStepAttribute(transformationId, stepId, "USERNAME",
+ password);
+ }
+ }
+
+ if (sslCaFile != null) {
+ rep.saveStepAttribute(transformationId, stepId, "SSL_CA_FILE",
+ sslCaFile);
+ }
+ if (sslCertFile != null) {
+ rep.saveStepAttribute(transformationId, stepId,
+ "SSL_CERT_FILE", sslCertFile);
+ }
+ if (sslKeyFile != null) {
+ rep.saveStepAttribute(transformationId, stepId, "SSL_KEY_FILE",
+ sslKeyFile);
+ }
+ if (sslKeyFilePass != null) {
+ rep.saveStepAttribute(transformationId, stepId,
+ "SSL_KEY_FILE_PASS", sslKeyFilePass);
+ }
} catch (Exception e) {
throw new KettleException("MQTTClientMeta.Exception.saveRep", e);
}
diff --git a/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerStep.java b/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerStep.java
index a11fe5a..a545163 100644
--- a/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerStep.java
+++ b/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/MQTTProducerStep.java
@@ -71,6 +71,25 @@ public boolean processRow(StepMetaInterface smi, StepDataInterface sdi)
data.client = new MqttClient(broker, clientId);
MqttConnectOptions connectOptions = new MqttConnectOptions();
+ if (meta.isRequiresAuth()) {
+ connectOptions.setUserName(environmentSubstitute(meta
+ .getUsername()));
+ connectOptions.setPassword(environmentSubstitute(
+ meta.getPassword()).toCharArray());
+ }
+ if (broker.startsWith("ssl:")) {
+ connectOptions
+ .setSocketFactory(SSLSocketFactoryGenerator
+ .getSocketFactory(
+ environmentSubstitute(meta
+ .getSSLCaFile()),
+ environmentSubstitute(meta
+ .getSSLCertFile()),
+ environmentSubstitute(meta
+ .getSSLKeyFile()),
+ environmentSubstitute(meta
+ .getSSLKeyFilePass())));
+ }
connectOptions.setCleanSession(true);
String timeout = environmentSubstitute(meta.getTimeout());
@@ -88,7 +107,7 @@ public boolean processRow(StepMetaInterface smi, StepDataInterface sdi)
clientId));
data.client.connect(connectOptions);
- } catch (MqttException e) {
+ } catch (Exception e) {
throw new KettleException(Messages.getString(
"MQTTClientStep.ErrorCreateMQTTClient.Message",
broker), e);
diff --git a/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/SSLSocketFactoryGenerator.java b/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/SSLSocketFactoryGenerator.java
new file mode 100644
index 0000000..4df2e76
--- /dev/null
+++ b/src/main/java/com/ruckuswireless/pentaho/mqtt/producer/SSLSocketFactoryGenerator.java
@@ -0,0 +1,88 @@
+package com.ruckuswireless.pentaho.mqtt.producer;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.Security;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMDecryptorProvider;
+import org.bouncycastle.openssl.PEMEncryptedKeyPair;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
+
+public class SSLSocketFactoryGenerator {
+
+ public static SSLSocketFactory getSocketFactory(String caCrtFile,
+ String crtFile, String keyFile, String password) throws Exception {
+
+ char[] passwordCharArray = password == null ? new char[0] : password
+ .toCharArray();
+
+ Security.addProvider(new BouncyCastleProvider());
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+ X509Certificate caCert = (X509Certificate) cf
+ .generateCertificate(new ByteArrayInputStream(Files
+ .readAllBytes(Paths.get(caCrtFile))));
+
+ X509Certificate cert = (X509Certificate) cf
+ .generateCertificate(new ByteArrayInputStream(Files
+ .readAllBytes(Paths.get(crtFile))));
+
+ File privateKeyFile = new File(keyFile);
+ PEMParser pemParser = new PEMParser(new FileReader(privateKeyFile));
+ PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder()
+ .build(passwordCharArray);
+ JcaPEMKeyConverter converter = new JcaPEMKeyConverter()
+ .setProvider("BC");
+
+ Object object = pemParser.readObject();
+ KeyPair kp;
+
+ if (object instanceof PEMEncryptedKeyPair) {
+ kp = converter.getKeyPair(((PEMEncryptedKeyPair) object)
+ .decryptKeyPair(decProv));
+ } else {
+ kp = converter.getKeyPair((PEMKeyPair) object);
+ }
+
+ pemParser.close();
+
+ KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ caKeyStore.load(null, null);
+ caKeyStore.setCertificateEntry("ca-certificate", caCert);
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory
+ .getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init(caKeyStore);
+
+ KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ keyStore.load(null, null);
+ keyStore.setCertificateEntry("certificate", cert);
+ keyStore.setKeyEntry("private-key", kp.getPrivate(), passwordCharArray,
+ new java.security.cert.Certificate[] { cert });
+ KeyManagerFactory keyManagerFactory = KeyManagerFactory
+ .getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ keyManagerFactory.init(keyStore, passwordCharArray);
+
+ SSLContext context = SSLContext.getInstance("TLSv1");
+ context.init(keyManagerFactory.getKeyManagers(),
+ trustManagerFactory.getTrustManagers(), null);
+
+ return context.getSocketFactory();
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/com/ruckuswireless/pentaho/mqtt/producer/messages/messages_en_US.properties b/src/main/resources/com/ruckuswireless/pentaho/mqtt/producer/messages/messages_en_US.properties
index 4959bed..fd0ffbb 100644
--- a/src/main/resources/com/ruckuswireless/pentaho/mqtt/producer/messages/messages_en_US.properties
+++ b/src/main/resources/com/ruckuswireless/pentaho/mqtt/producer/messages/messages_en_US.properties
@@ -18,12 +18,31 @@ MQTTClientMeta.Check.InvalidField=Field name must be set\!
MQTTClientMeta.Check.InvalidClientID=Client ID must be set\!
MQTTClientMeta.Check.InvalidConnectionTimeout=Connection timeout must be set\!
MQTTClientMeta.Check.InvalidQOS=QoS must be set\!
+MQTTClientMeta.Check.InvalidUsername=Username must be set\!
+MQTTClientMeta.Check.InvalidPassword=Password must be set\!
MQTTClientDialog.Shell.Title=MQTT Client
MQTTClientDialog.StepName.Label=Step name
+MQTTClientDialog.GeneralTab.Label=General
MQTTClientDialog.Broker.Label=Broker URL
MQTTClientDialog.TopicName.Label=Topic name
MQTTClientDialog.FieldName.Label=Input field name
+MQTTClientDialog.FieldName.Tooltip=Name of the field containing the message to be sent over MQTT
MQTTClientDialog.ClientID.Label=Client ID
+MQTTClientDialog.RequireAuth.Label=Server requires authorization
+MQTTClientDialog.Username.Label=Username
+MQTTClientDialog.Username.Tooltip=Username for connecting to MQTT bridge
+MQTTClientDialog.Password.Label=Password
+MQTTClientDialog.Password.Tooltip=Password for connecting to MQTT bridge
+MQTTClientDialog.CredentialsTab.Title=Credentials
MQTTClientDialog.ConnectionTimeout.Label=Connection timeout
MQTTClientDialog.QOS.Label=QoS
+MQTTClientDialog.SSLTab.Label=SSL
+MQTTClientDialog.CAFile.Label=CA file path
+MQTTClientDialog.CAFile.Tooltip=Please specify path to the server CA file
+MQTTClientDialog.CertFile.Label=Certificate file path
+MQTTClientDialog.CertFile.Tooltip=Please specify path to the client certificate file
+MQTTClientDialog.KeyFile.Label=Key file path
+MQTTClientDialog.KeyFile.Tooltip=Please specify path to the client key file
+MQTTClientDialog.KeyPassword.Label=Key file password
+MQTTClientDialog.KeyPassword.Tooltip=Please specify client key file password if any
MQTTClientDialog.ErrorDialog.UnableToGetInputFields=Unable to get input fields for this step\!
\ No newline at end of file
diff --git a/src/main/resources/plugin.xml b/src/main/resources/plugin.xml
index 61a6acb..cfa6b59 100644
--- a/src/main/resources/plugin.xml
+++ b/src/main/resources/plugin.xml
@@ -10,6 +10,8 @@
+
+