/*
* JBoss, Home of Professional Open Source
* Copyright 2005-2008, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.test.messaging.jms.stress;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.naming.InitialContext;
import org.jboss.messaging.jms.client.JBossConnectionFactory;
import org.jboss.test.messaging.JBMServerTestCase;
import org.jboss.util.id.GUID;
/**
*
* A OpenCloseStressTest.
*
* This stress test starts several publisher connections and several subscriber connections, then sends and consumes
* messages while concurrently closing the sessions.
*
* This test will help catch race conditions that occurred with rapid open/closing of sessions when messages are being
* sent/received
*
* E.g. http://jira.jboss.com/jira/browse/JBMESSAGING-982
*
* @author <a href="tim.fox@jboss.com">Tim Fox</a>
* @version <tt>$Revision: 2349 $</tt>
*
* $Id: StressTest.java 2349 2007-02-19 14:15:53Z timfox $
*/
public class OpenCloseStressTest extends JBMServerTestCase
{
public OpenCloseStressTest(String name)
{
super(name);
}
InitialContext ic;
JBossConnectionFactory cf;
Topic topic;
public void setUp() throws Exception
{
super.setUp();
//ServerManagement.start("all");
ic = getInitialContext();
cf = (JBossConnectionFactory)ic.lookup("/ConnectionFactory");
destroyTopic("TestTopic");
createTopic("TestTopic");
topic = (Topic) ic.lookup("topic/TestTopic");
log.debug("setup done");
}
public void tearDown() throws Exception
{
destroyQueue("TestQueue");
super.tearDown();
log.debug("tear down done");
}
public void testOpenClose() throws Exception
{
Connection conn1 = null;
Connection conn2 = null;
Connection conn3 = null;
Connection conn4 = null;
Connection conn5 = null;
Connection conn6 = null;
Connection conn7 = null;
Connection conn8 = null;
try
{
Publisher[] publishers = new Publisher[3];
final int MSGS_PER_PUBLISHER = 10000;
conn1 = cf.createConnection();
Session sess1 = conn1.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer prod1 = sess1.createProducer(topic);
prod1.setDeliveryMode(DeliveryMode.PERSISTENT);
publishers[0] = new Publisher(sess1, prod1, MSGS_PER_PUBLISHER, 2);
conn2 = cf.createConnection();
Session sess2 = conn2.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer prod2 = sess2.createProducer(topic);
prod2.setDeliveryMode(DeliveryMode.PERSISTENT);
publishers[1] = new Publisher(sess2, prod2, MSGS_PER_PUBLISHER, 5);
conn3 = cf.createConnection();
Session sess3 = conn3.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer prod3 = sess3.createProducer(topic);
prod3.setDeliveryMode(DeliveryMode.PERSISTENT);
publishers[2] = new Publisher(sess3, prod3, MSGS_PER_PUBLISHER, 1);
Subscriber[] subscribers = new Subscriber[5];
conn4 = cf.createConnection();
subscribers[0] = new Subscriber(conn4, 3 * MSGS_PER_PUBLISHER, 500, 1000 * 60 * 15, topic, false);
conn5 = cf.createConnection();
subscribers[1] = new Subscriber(conn5, 3 * MSGS_PER_PUBLISHER, 2000, 1000 * 60 * 15, topic, false);
conn6 = cf.createConnection();
subscribers[2] = new Subscriber(conn6, 3 * MSGS_PER_PUBLISHER, 700, 1000 * 60 * 15, topic, false);
conn7 = cf.createConnection();
subscribers[3] = new Subscriber(conn7, 3 * MSGS_PER_PUBLISHER, 1500, 1000 * 60 * 15, topic, true);
conn8 = cf.createConnection();
subscribers[4] = new Subscriber(conn8, 3 * MSGS_PER_PUBLISHER, 1200, 1000 * 60 * 15, topic, true);
Thread[] threads = new Thread[8];
//subscribers
threads[0] = new Thread(subscribers[0]);
threads[1] = new Thread(subscribers[1]);
threads[2] = new Thread(subscribers[2]);
threads[3] = new Thread(subscribers[3]);
threads[4] = new Thread(subscribers[4]);
//publishers
threads[5] = new Thread(publishers[0]);
threads[6] = new Thread(publishers[1]);
threads[7] = new Thread(publishers[2]);
for (int i = 0; i < subscribers.length; i++)
{
threads[i].start();
}
// Pause before creating producers otherwise subscribers to make sure they're all created
Thread.sleep(5000);
for (int i = subscribers.length; i < threads.length; i++)
{
threads[i].start();
}
for (int i = 0; i < threads.length; i++)
{
threads[i].join();
}
for (int i = 0; i < subscribers.length; i++)
{
if (subscribers[i].isDurable())
{
assertEquals(3 * MSGS_PER_PUBLISHER, subscribers[i].getMessagesReceived());
}
else
{
//Note that for a non durable subscriber the number of messages received in total
//will be somewhat less than the total number received since when recycling the session
//there is a period of time after closing the previous session and starting the next one
//when messages are being sent and won't be received (since there is no consumer)
}
assertFalse(subscribers[i].isFailed());
}
for (int i = 0; i < publishers.length; i++)
{
assertFalse(publishers[i].isFailed());
}
}
finally
{
if (conn1 != null)
{
conn1.close();
}
if (conn2 != null)
{
conn2.close();
}
if (conn3 != null)
{
conn3.close();
}
if (conn4 != null)
{
conn4.close();
}
if (conn5 != null)
{
conn5.close();
}
if (conn6 != null)
{
conn6.close();
}
if (conn7 != null)
{
conn7.close();
}
if (conn8 != null)
{
conn8.close();
}
}
}
class Publisher implements Runnable
{
private Session sess;
private int numMessages;
private int delay;
private MessageProducer prod;
private boolean failed;
boolean isFailed()
{
return failed;
}
Publisher(Session sess, MessageProducer prod, int numMessages, int delay)
{
this.sess = sess;
this.prod = prod;
this.numMessages = numMessages;
this.delay = delay;
}
public void run()
{
try
{
for (int i = 0; i < numMessages; i++)
{
TextMessage tm = sess.createTextMessage("message" + i);
prod.send(tm);
try
{
Thread.sleep(delay);
}
catch (Exception ignore)
{
}
}
}
catch (JMSException e)
{
log.error("Failed to send message", e);
failed = true;
}
}
}
class Subscriber implements Runnable
{
private Session sess;
private MessageConsumer cons;
private int msgsReceived;
private int numMessages;
private int delay;
private Connection conn;
private boolean failed;
private long timeout;
private Destination dest;
private boolean durable;
private String subname;
boolean isFailed()
{
return failed;
}
boolean isDurable()
{
return durable;
}
synchronized void msgReceived()
{
msgsReceived++;
}
synchronized int getMessagesReceived()
{
return msgsReceived;
}
class Listener implements MessageListener
{
public void onMessage(Message msg)
{
msgReceived();
}
}
Subscriber(Connection conn, int numMessages, int delay, long timeout, Destination dest, boolean durable) throws Exception
{
this.conn = conn;
this.numMessages = numMessages;
this.delay = delay;
this.timeout = timeout;
this.dest = dest;
this.durable = durable;
if (durable)
{
conn.setClientID(new GUID().toString());
this.subname = new GUID().toString();
}
}
public void run()
{
try
{
long start = System.currentTimeMillis();
while (((System.currentTimeMillis() - start) < timeout) && msgsReceived < numMessages)
{
//recycle the session
recycleSession();
Thread.sleep(delay);
}
//Delete the durable sub
if (durable)
{
recycleSession();
cons.close();
sess.unsubscribe(subname);
}
}
catch (Exception e)
{
log.error("Failed in subscriber", e);
failed = true;
}
}
void recycleSession() throws Exception
{
conn.stop();
if (sess != null)
{
sess.close();
}
sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
if (durable)
{
cons = sess.createDurableSubscriber((Topic)dest, subname);
}
else
{
cons = sess.createConsumer(dest);
}
cons.setMessageListener(new Listener());
conn.start();
}
}
}