package com.blazemeter.jmeter.xmpp;
import com.blazemeter.jmeter.xmpp.actions.*;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.bosh.BOSHConfiguration;
import org.jivesoftware.smack.bosh.XMPPBOSHConnection;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import java.io.IOException;
import java.io.Serializable;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class JMeterXMPPConnection extends JMeterXMPPConnectionBase {
private static final Logger log = LoggingManager.getLoggerForClass();
private static final BlockingQueue<XMPPConnection> connectionRegistry = new LinkedBlockingQueue<>();
private XMPPConnection conn;
private Map<String, AbstractXMPPAction> actions = getAvailableActions();
public JMeterXMPPConnection() {
super();
}
@Override
public void testEnded(String host) {
log.debug("Test ended: " + host);
for (XMPPConnection conn : connectionRegistry) {
connectionRegistry.remove(conn);
if (conn.isConnected()) {
try {
log.debug("Disconnecting: " + conn.getConnectionID());
conn.disconnect();
} catch (SmackException.NotConnectedException e) {
log.error("Not connected, nothing to disconnect");
}
}
}
}
/**
* Creates new connection or returns previously created
*
* @return XMPPConnection
*/
public XMPPConnection getConnection() throws NoSuchAlgorithmException, KeyManagementException, SmackException, InterruptedException {
if (conn == null) {
String address = getAddress();
String serv_name = getServiceName();
if (serv_name.isEmpty()) serv_name = address;
int port = Integer.parseInt(getPort());
log.debug("Creating connection: " + address + ":" + port + "/" + serv_name);
XMPPConnection newConn;
if (Type.valueOf(getConnectionType()) == Type.BOSH) {
BOSHConfiguration conf = new BOSHConfiguration(isBOSHSSL(), address, port, getBOSHURL(), serv_name);
conf.setCustomSSLContext(getSSLContext());
conf.setRosterLoadedAtLogin(true);
conf.setSendPresence(false);
newConn = new XMPPBOSHConnection(conf);
} else {
ConnectionConfiguration conf = new ConnectionConfiguration(address, port, serv_name);
conf.setRosterLoadedAtLogin(true);
conf.setSendPresence(false);
// accept all TLS certs
conf.setCustomSSLContext(getSSLContext());
newConn = new XMPPTCPConnection(conf);
}
connectionRegistry.put(newConn);
setUpConnection(newConn);
}
return conn;
}
private void setUpConnection(XMPPConnection newConn) {
conn = newConn;
conn.setPacketReplyTimeout(Integer.parseInt(getPacketReplyTimeout()));
conn.setFromMode(getFromMode());
if (log.isDebugEnabled()) {
conn.addConnectionListener(new Loggers.LogConn(conn));
conn.addPacketListener(new Loggers.LogRecv(conn), new AndFilter());
conn.addPacketSendingListener(new Loggers.LogSent(conn), new AndFilter());
}
for (AbstractXMPPAction action : actions.values()) {
if (action instanceof PacketListener) {
conn.addPacketListener((PacketListener) action, action.getPacketFilter());
}
if (action instanceof ConnectionListener) {
conn.addConnectionListener((ConnectionListener) action);
}
}
}
public static Map<String, AbstractXMPPAction> getAvailableActions() {
Map<String, AbstractXMPPAction> actions = new TreeMap<>(new OrderComparator());
try {
for (String cls : JMeterUtils.findClassesThatExtend(AbstractXMPPAction.class)) {
actions.put(cls, (AbstractXMPPAction) Class.forName(cls).newInstance());
}
} catch (IOException | InstantiationException | IllegalAccessException | ClassNotFoundException e) {
log.error("Error loading actions", e);
}
return actions;
}
public Map<String, AbstractXMPPAction> getActions() {
return actions;
}
@Override
public void resetConnection() {
connectionRegistry.remove(conn);
conn = null;
}
private static class OrderComparator implements Comparator<String>, Serializable {
private static final List<String> fixed = Arrays.asList(
Connect.class.getCanonicalName(),
Login.class.getCanonicalName(),
RosterAction.class.getCanonicalName(),
SendPresence.class.getCanonicalName(),
SendMessage.class.getCanonicalName(),
RawXML.class.getCanonicalName(),
NoOp.class.getCanonicalName(),
Disconnect.class.getCanonicalName()
);
@Override
public int compare(String s1, String s2) {
if (s1.equals(s2)) return 0;
int i1 = fixed.indexOf(s1);
int i2 = fixed.indexOf(s2);
if (i1 < 0 && i2 < 0)
return s1.compareTo(s2);
else if (i1 < 0)
return 1;
else if (i2 < 0)
return -1;
else
return i1 > i2 ? 1 : -1;
}
}
}