/**
* CopyRight by Chinamobile
*
* ProducerTool.java
*/
package com.chinamobile.bcbsp.comm;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.ObjectMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Producer tool for sending messages to Message Queue.
*
* @author
* @version
*/
public class ProducerTool extends Thread {
// For log
private static final Log LOG = LogFactory.getLog(ProducerTool.class);
// For time
private long ConnectTime = 0;/**Clock*/
private long SendTime = 0;/**Clock*/
// For retry
private static final int ReconnectThreshold = 10;
private int reconnectCount = 0;
private int packSize = 100; //Default 100 messages for a package
private Connection connection = null;
private Session session = null;
private Destination destination = null;
private MessageProducer producer = null;
private long sleepTime = 500;
private long timeToLive = 0;
private String user = ActiveMQConnection.DEFAULT_USER;
private String password = ActiveMQConnection.DEFAULT_PASSWORD;
private String url = null; // "tcp://hostName:port"
private boolean transacted = false;
private boolean persistent = false;
private long messageCount = 0; // Total count.
private boolean isFailed = false;
// Message queue for sending.
private ConcurrentLinkedQueue<BSPMessage> messageQueue = null;
private String hostNameAndPort = null;
private boolean newHostNameAndPort = false;
private String subject = null;
private volatile boolean idle = true;
private volatile boolean noMoreMessagesFlag = false;
private volatile int superStepCounter = -1;
private volatile boolean completed = false;
private Sender sender = null;
/**
* Constructor
*
* @param queue
* @param hostNameAndPort
* @param subject
*/
public ProducerTool(ThreadGroup group, int sn, ConcurrentLinkedQueue<BSPMessage> queue,
String hostNameAndPort, String subject, Sender sender) {
super(group, "ProducerTool-" + sn);
this.messageQueue = queue;
this.hostNameAndPort = hostNameAndPort;
this.newHostNameAndPort = true;
this.subject = subject; // always = "BSP"
this.sender = sender;
}
public void addMessages(ConcurrentLinkedQueue<BSPMessage> messages) {
messageQueue = messages;
}
public void setHostNameAndPort(String hostNameAndPort) {
if (!hostNameAndPort.equals(this.hostNameAndPort)) {
this.hostNameAndPort = hostNameAndPort;
this.newHostNameAndPort = true;
}
}
public void setPackSize(int size) {
this.packSize = size;
}
public boolean isIdle() {
return idle;
}
public void setIdle(boolean state) {
this.idle = state;
}
public void setNoMoreMessagesFlag(boolean flag) {
this.noMoreMessagesFlag = flag;
}
public int getProgress() {
return this.superStepCounter;
}
public void setProgress(int superStepCount) {
this.superStepCounter = superStepCount - 1;
}
public void complete() {
this.completed = true;
}
public void showParameters() {
LOG.info("Connecting to URL: " + url);
LOG.info("Publishing Messages " + "to queue: " + subject);
LOG.info("Using "
+ (persistent ? "persistent" : "non-persistent") + " messages");
LOG.info("Sleeping between publish " + sleepTime + " ms");
if (timeToLive != 0) {
LOG.info("Messages time to live " + timeToLive + " ms");
}
}
public void run() {
while (true) {
while (this.idle) {
if (this.completed) {
return;
}
if (this.noMoreMessagesFlag) {
this.superStepCounter ++;
this.noMoreMessagesFlag = false;
//LOG.info("Test Progress: from " + (this.superStepCounter - 1) + " to " + this.superStepCounter);
}
try {
Thread.sleep(this.sleepTime);
} catch (InterruptedException e) {
LOG.error("[ProducerTool] to " + this.hostNameAndPort + " has been interrupted for ", e);
return;
}
}
if (this.hostNameAndPort == null) {
LOG.error("Destination hostname is null.");
return;
}
if (this.messageQueue == null) {
LOG.error("Message queue for ProducerTool is null.");
return;
}
this.messageCount = 0;
this.ConnectTime = 0;
this.SendTime = 0;
while (true) {
if (this.reconnectCount == ProducerTool.ReconnectThreshold) {
break;
}
try {
if (this.newHostNameAndPort) { //Should create new connection.
if (connection != null) {//Close early connection first.
try {
connection.close();
} catch (Throwable ignore) {
}
}
long start = System.currentTimeMillis();/**Clock*/
// Make the destination broker's url.
this.url = "tcp://" + this.hostNameAndPort;
// Create the connection.
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
user, password, url);
connectionFactory.setCopyMessageOnSend(false);
connection = connectionFactory.createConnection();
connection.start();
// Create the session
session = connection.createSession(transacted, Session.AUTO_ACKNOWLEDGE);
this.ConnectTime += (System.currentTimeMillis() - start);/**Clock*/
this.newHostNameAndPort = false;
start = System.currentTimeMillis();/**Clock*/
destination = session.createQueue(subject);
// Create the producer.
producer = session.createProducer(destination);
if (persistent) {
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
} else {
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
}
if (timeToLive != 0) {
producer.setTimeToLive(timeToLive);
}
this.ConnectTime += (System.currentTimeMillis() - start);/**Clock*/
}
// Start sending messages
sendLoop(session, producer);
this.idle = true;
break;
} catch (Exception e) {
this.reconnectCount ++;
if (this.reconnectCount == 1)
LOG.error("[ProducerTool] to " + this.hostNameAndPort + " caught: ", e);
LOG.info("[ProducerTool] to " + this.hostNameAndPort + " is reconnecting for "
+ this.reconnectCount + "th time.");
LOG.info("---------------- Memory Info ------------------");
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
MemoryUsage memoryUsage = memoryMXBean.getHeapMemoryUsage();
long used = memoryUsage.getUsed();
long committed = memoryUsage.getCommitted();
LOG.info("[JVM Memory used] = " + used/1048576 + "MB");
LOG.info("[JVM Memory committed] = " + committed/1048576 + "MB");
LOG.info("-----------------------------------------------");
try {
Thread.sleep(this.sleepTime);
} catch (InterruptedException e1) {
LOG.error("[ProducerTool] caught: ", e1);
}
}//end-try
}//end-while
LOG.info("[ProducerTool] to " + this.hostNameAndPort + " has sent " + this.messageCount
+ " messages totally! (with " + this.messageQueue.size() + " messages lost!)");
this.sender.addConnectTime(this.ConnectTime);/**Clock*/
this.sender.addSendTime(this.SendTime);/**Clock*/
if (this.reconnectCount == ProducerTool.ReconnectThreshold) {
LOG.info("[ProducerTool] to " + this.hostNameAndPort + " has reconnected for "
+ this.reconnectCount + " times but failed!");
this.isFailed = true;
break;
}
}//end-while
}//end-run
public boolean isFailed() {
return this.isFailed;
}
private void sendLoop(Session session, MessageProducer producer)
throws Exception {
BSPMessage msg;
int count = 0;
BSPMessagesPack pack = new BSPMessagesPack();
ArrayList<BSPMessage> content = new ArrayList<BSPMessage>();
while ((msg = messageQueue.poll()) != null) {
content.add(msg);
count ++;
this.messageCount ++;
// enough for a pack.
if (count == this.packSize) {
pack.setPack(content);
long start = System.currentTimeMillis();/**Clock*/
ObjectMessage message = session.createObjectMessage(pack);
producer.send(message);
this.SendTime += (System.currentTimeMillis() - start);/**Clock*/
content.clear();
count = 0;
}
}//end-while
// remaining messages into a pack.
if (content.size() > 0) {
pack.setPack(content);
long start = System.currentTimeMillis();/**Clock*/
ObjectMessage message = session.createObjectMessage(pack);
producer.send(message);
this.SendTime += (System.currentTimeMillis() - start);/**Clock*/
content.clear();
}
}//end-sendLoop
}