package com.blazemeter.jmeter.xmpp.actions; import com.blazemeter.jmeter.xmpp.JMeterXMPPSampler; import org.apache.jmeter.samplers.SampleResult; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; import org.jivesoftware.smack.ConnectionListener; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smackx.delay.packet.DelayInfo; import org.jivesoftware.smackx.delay.packet.DelayInformation; import javax.swing.*; import java.awt.*; import java.util.Date; import java.util.Iterator; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; public class SendMessage extends AbstractXMPPAction implements PacketListener, ConnectionListener { private static final Logger log = LoggingManager.getLoggerForClass(); public static final String RECIPIENT = "msg_w_resp_addressee"; public static final String BODY = "msg_w_resp_body"; public static final String WAIT_RESPONSE = "wait_response"; public static final String TYPE = "msg_type"; public static final String NEED_RESPONSE_MARKER = "ExpectedResponseMarker"; public static final String RESPONSE_MARKER = "ProvidedResponseMarker"; private final static String NS_DELAYED = (new DelayInfo(new DelayInformation(new Date()))).getNamespace(); private JTextField msgRecipient; private JTextArea msgBody; private JCheckBox waitResponse; private JComboBox<Message.Type> msgType; private Queue<Message> responseMessages = new LinkedBlockingQueue<>(); private XMPPConnection conn; @Override public String getLabel() { return "Send Message"; } @Override public SampleResult perform(JMeterXMPPSampler sampler, SampleResult res) throws Exception { // sending message String recipient = sampler.getPropertyAsString(RECIPIENT); String body = sampler.getPropertyAsString(BODY); boolean wait_response = sampler.getPropertyAsBoolean(WAIT_RESPONSE); if (wait_response) { body += "\r\n" + System.currentTimeMillis() + "@" + NEED_RESPONSE_MARKER; } Message msg = new Message(recipient); msg.setType(Message.Type.fromString(sampler.getPropertyAsString(TYPE, Message.Type.normal.toString()))); msg.addBody("", body); res.setSamplerData(msg.toXML().toString()); sampler.getXMPPConnection().sendPacket(msg); res.setSamplerData(msg.toXML().toString()); // second time to reflect the changes made to packet by conn if (wait_response) { return waitResponse(res, recipient); } return res; } private SampleResult waitResponse(SampleResult res, String recipient) throws InterruptedException, SmackException { long time = 0; do { Iterator<Message> packets = responseMessages.iterator(); Thread.sleep(conn.getPacketReplyTimeout() / 100); // optimistic while (packets.hasNext()) { Packet packet = packets.next(); Message response = (Message) packet; if (StringUtils.parseBareAddress(response.getFrom()).equals(recipient)) { packets.remove(); res.setResponseData(response.toXML().toString().getBytes()); if (response.getError() != null) { res.setSuccessful(false); res.setResponseCode("500"); res.setResponseMessage(response.getError().toString()); } return res; } } time += conn.getPacketReplyTimeout() / 10; Thread.sleep(conn.getPacketReplyTimeout() / 10); } while (time < conn.getPacketReplyTimeout()); throw new SmackException.NoResponseException(); } @Override public void addUI(JComponent mainPanel, GridBagConstraints labelConstraints, GridBagConstraints editConstraints) { addToPanel(mainPanel, labelConstraints, 0, 0, new JLabel("Type: ", JLabel.RIGHT)); addToPanel(mainPanel, editConstraints, 1, 0, msgType = new JComboBox<>()); msgType.addItem(Message.Type.normal); msgType.addItem(Message.Type.chat); msgType.addItem(Message.Type.groupchat); msgType.addItem(Message.Type.headline); msgType.addItem(Message.Type.error); addToPanel(mainPanel, labelConstraints, 0, 1, new JLabel("Recipient: ", JLabel.RIGHT)); addToPanel(mainPanel, editConstraints, 1, 1, msgRecipient = new JTextField(20)); addToPanel(mainPanel, labelConstraints, 0, 2, new JLabel("Message Text: ", JLabel.RIGHT)); addToPanel(mainPanel, editConstraints, 1, 2, msgBody = new JTextArea(5, 20)); addToPanel(mainPanel, labelConstraints, 0, 3, new JLabel("Wait for Response: ", JLabel.RIGHT)); addToPanel(mainPanel, editConstraints, 1, 3, waitResponse = new JCheckBox("(message that expects response should be auto-responded by another JMeter thread)")); } @Override public void clearGui() { msgRecipient.setText(""); msgBody.setText(""); waitResponse.setSelected(false); msgType.setSelectedIndex(0); } @Override public void setSamplerProperties(JMeterXMPPSampler sampler) { sampler.setProperty(RECIPIENT, msgRecipient.getText()); sampler.setProperty(BODY, msgBody.getText()); sampler.setProperty(WAIT_RESPONSE, waitResponse.isSelected()); sampler.setProperty(TYPE, msgType.getSelectedItem().toString()); } @Override public void setGuiFieldsFromSampler(JMeterXMPPSampler sampler) { msgRecipient.setText(sampler.getPropertyAsString(RECIPIENT)); msgBody.setText(sampler.getPropertyAsString(BODY)); waitResponse.setSelected(sampler.getPropertyAsBoolean(WAIT_RESPONSE)); msgType.setSelectedItem(Message.Type.fromString(sampler.getPropertyAsString(TYPE, Message.Type.normal.toString()))); } @Override public void processPacket(Packet packet) throws SmackException.NotConnectedException { if (packet instanceof Message) { Message inMsg = (Message) packet; if (inMsg.getBody() != null) { if (inMsg.getBody().endsWith(NEED_RESPONSE_MARKER)) { if (inMsg.getExtension(NS_DELAYED) == null) { log.debug("Will respond to message: " + inMsg.toXML()); sendResponseMessage(inMsg); } else { log.debug("Will not consider history message: " + inMsg.toXML()); } } else if (inMsg.getBody().endsWith(RESPONSE_MARKER)) { responseMessages.add(inMsg); } } } } private void sendResponseMessage(Message inMsg) { Message outMsg = new Message(inMsg.getFrom()); outMsg.setType(inMsg.getType()); outMsg.addBody("", inMsg.getBody() + "\r\n" + System.currentTimeMillis() + "@" + RESPONSE_MARKER); log.debug("Responding to message: " + outMsg.toXML()); try { conn.sendPacket(outMsg); } catch (SmackException e) { log.error("Failed to send response", e); } } @Override public void connected(XMPPConnection connection) { this.conn = connection; } @Override public void authenticated(XMPPConnection connection) { } @Override public void connectionClosed() { } @Override public void connectionClosedOnError(Exception e) { } @Override public void reconnectingIn(int seconds) { } @Override public void reconnectionSuccessful() { } @Override public void reconnectionFailed(Exception e) { } }