/**
*
* Copyright 2004 Protique Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**/
package org.activemq.test;
import java.io.IOException;
import javax.jms.JMSException;
import javax.jms.TextMessage;
import junit.framework.TestCase;
import org.activemq.broker.BrokerClient;
import org.activemq.broker.impl.BrokerClientImpl;
import org.activemq.filter.DestinationFilter;
import org.activemq.filter.FilterFactoryImpl;
import org.activemq.message.ActiveMQDestination;
import org.activemq.message.ActiveMQMessage;
import org.activemq.message.ActiveMQTextMessage;
import org.activemq.message.ConsumerInfo;
import org.activemq.service.DeadLetterPolicy;
import org.activemq.service.MessageContainer;
import org.activemq.service.MessageContainerManager;
import org.activemq.service.MessageIdentity;
import org.activemq.service.RedeliveryPolicy;
import org.activemq.service.Subscription;
import org.activemq.service.impl.DispatcherImpl;
import org.activemq.service.impl.DurableQueueMessageContainer;
import org.activemq.service.impl.DurableQueueMessageContainerManager;
import org.activemq.service.impl.DurableTopicMessageContainer;
import org.activemq.service.impl.DurableTopicMessageContainerManager;
import org.activemq.service.impl.DurableTopicSubscription;
import org.activemq.service.impl.DurableTopicSubscriptionContainerImpl;
import org.activemq.store.PersistenceAdapter;
import org.activemq.util.Callback;
import org.activemq.util.IdGenerator;
import org.activemq.util.TransactionTemplate;
/**
* @version $Revision: 1.1.1.1 $
*/
public abstract class MessageStoreTestSupport extends TestCase {
protected PersistenceAdapter persistenceAapter;
protected MessageContainer container;
protected Subscription subscription;
protected int publishMessageCount = 10;
protected int ackCount = 5;
protected ActiveMQMessage[] messages;
protected ActiveMQDestination destination;
protected IdGenerator idGenerator = new IdGenerator();
protected MessageContainerManager messageContainerManager;
protected BrokerClient client = new BrokerClientImpl();
protected TransactionTemplate template;
public void testRecovery() throws Exception {
System.out.println("Publishing: " + publishMessageCount + " messages");
for (int i = 0; i < publishMessageCount; i++) {
doAddMessage(i);
}
dumpMessageIdentities("After add");
assertDeliveryList(0, publishMessageCount);
// now lets ack messages
System.out.println("Acknowledging the first: " + ackCount + " messages");
for (int i = 0; i < ackCount; i++) {
doAcknowledgeMessage(i);
}
// no more left to dispatch
assertDeliveryList(0, 0);
dumpMessageIdentities("After ack of first part");
// now lets shut things down and then recover
closeAndReopenContainer();
assertDeliveryList(ackCount, publishMessageCount);
dumpMessageIdentities("About to perform final ack");
for (int i = ackCount; i < publishMessageCount; i++) {
doAcknowledgeMessage(i);
}
}
public void testRecoveryOfNewConsumerWhichHasYetToAck() throws Exception {
for (int i = 0; i < publishMessageCount; i++) {
doAddMessage(i);
}
assertDeliveryList(0, publishMessageCount);
// no more left to dispatch
assertDeliveryList(0, 0);
// now lets shut things down and then recover
closeAndReopenContainer();
assertDeliveryList(0, publishMessageCount);
}
protected abstract void acknowledgeMessage(int i) throws JMSException;
protected abstract PersistenceAdapter createPersistenceAdapter() throws IOException, Exception;
protected abstract ActiveMQDestination createDestination();
protected abstract ActiveMQMessage[] getMessagesToDispatch() throws JMSException;
protected void doAcknowledgeMessage(final int i) throws JMSException {
template.run(new Callback() {
public void execute() throws Throwable {
acknowledgeMessage(i);
}
});
}
protected void doAddMessage(int i) throws JMSException {
final ActiveMQMessage message = getMessage(i);
template.run(new Callback() {
public void execute() throws Throwable {
container.addMessage(message);
}
});
}
protected void dumpMessageIdentities(String text) throws JMSException {
System.out.println("#### Dumping identities at: " + text);
for (int i = 0; i < publishMessageCount; i++) {
ActiveMQMessage message = getMessage(i);
MessageIdentity identity = message.getJMSMessageIdentity();
Object sequenceNo = identity.getSequenceNumber();
String sequenceText = null;
if (sequenceNo != null) {
sequenceText = toStringFromSequenceNumber(sequenceNo);
}
System.out.println("item: " + i + " is: " + sequenceText);
}
System.out.println();
}
protected String toStringFromSequenceNumber(Object sequenceNo) {
return sequenceNo.toString();
}
protected String asText(byte[] data) {
StringBuffer buffer = new StringBuffer("[ ");
for (int i = 0; i < data.length; i++) {
if (i > 0) {
buffer.append(", ");
}
buffer.append(Byte.toString(data[i]));
}
buffer.append(" ]");
return buffer.toString();
}
protected MessageContainer createTopicMessageContainer() throws JMSException {
if (destination.isTopic()) {
return new DurableTopicMessageContainer(null, persistenceAapter.createTopicMessageStore(destination.toString()), destination.toString());
}
else {
return new DurableQueueMessageContainer(persistenceAapter, persistenceAapter.createQueueMessageStore(destination.toString()), destination.toString());
}
}
protected Subscription createSubscription() throws JMSException {
DestinationFilter filter = DestinationFilter.parseFilter(destination);
ConsumerInfo consumerInfo = createConsumerInfo();
// lets register the subscription with the manager
messageContainerManager.addMessageConsumer(client, consumerInfo);
return new DurableTopicSubscription(new DispatcherImpl(), client, consumerInfo, filter, new RedeliveryPolicy(),new DeadLetterPolicy());
}
protected ConsumerInfo createConsumerInfo() {
ConsumerInfo answer = new ConsumerInfo();
answer.setClientId(getClientID());
answer.setConsumerId(idGenerator.generateId());
answer.setConsumerName(getConsumerName());
answer.setDestination(destination);
answer.setPrefetchNumber(100);
answer.setSessionId((short)123);
answer.setStarted(true);
return answer;
}
protected String getConsumerName() {
return getName();
}
protected String getClientID() {
return getClass().getName();
}
protected void setUp() throws Exception {
super.setUp();
this.messages = new ActiveMQMessage[publishMessageCount];
this.destination = createDestination();
this.persistenceAapter = createPersistenceAdapter();
persistenceAapter.start();
template = new TransactionTemplate(persistenceAapter);
this.messageContainerManager = createMessageContainerManager();
this.container = messageContainerManager.getContainer(this.destination.getPhysicalName());
assertTrue("Should have created a container", container != null);
this.subscription = createSubscription();
}
protected void tearDown() throws Exception {
messageContainerManager.destroyMessageContainer(destination);
messageContainerManager.stop();
persistenceAapter.stop();
super.tearDown();
}
protected MessageContainerManager createMessageContainerManager() {
if (destination.isTopic()) {
return new DurableTopicMessageContainerManager(persistenceAapter, new DurableTopicSubscriptionContainerImpl(new RedeliveryPolicy(),new DeadLetterPolicy()), new FilterFactoryImpl(), new DispatcherImpl());
}
else {
return new DurableQueueMessageContainerManager(persistenceAapter, new DurableTopicSubscriptionContainerImpl(new RedeliveryPolicy(),new DeadLetterPolicy()), new FilterFactoryImpl(), new DispatcherImpl());
}
}
protected void assertDeliveryList(final int startIndex, final int lastIndex) throws JMSException {
template.run(new Callback() {
public void execute() throws Throwable {
ActiveMQMessage[] messagesToDispatch = getMessagesToDispatch();
int count = lastIndex - startIndex;
assertTrue("Not enough messages available to dispatch. Expected: " + count
+ " messages but was: " + messagesToDispatch.length, messagesToDispatch.length >= count);
for (int i = 0; i < count; i++) {
ActiveMQMessage expected = getMessage(i + startIndex);
ActiveMQMessage actual = messagesToDispatch[i];
assertMessagesEqual("Dispatched message at index: " + i, expected, actual);
}
}
});
}
protected void assertMessagesEqual(String description, ActiveMQMessage expected, ActiveMQMessage actual) throws JMSException {
assertEquals("MessageText compare. " + description, ((TextMessage) expected).getText(), ((TextMessage) actual).getText());
assertEquals("MessageID compare. " + description + " expected: " + expected + " actual: " + actual, expected.getJMSMessageID(), actual.getJMSMessageID());
assertEquals(description, expected, actual);
}
protected ActiveMQMessage getMessage(int i) throws JMSException {
if (messages[i] == null) {
messages[i] = createMessage(i);
}
return messages[i];
}
protected ActiveMQMessage createMessage(int i) throws JMSException {
ActiveMQTextMessage answer = new ActiveMQTextMessage();
answer.setJMSMessageID(idGenerator.generateId());
answer.setJMSClientID(getClientID());
answer.setJMSDestination(destination);
answer.setText("message index: " + i);
return answer;
}
protected void closeAndReopenContainer() throws Exception {
subscription.clear();
messageContainerManager.stop();
persistenceAapter.stop();
persistenceAapter = createPersistenceAdapter();
persistenceAapter.start();
template = new TransactionTemplate(persistenceAapter);
this.messageContainerManager = createMessageContainerManager();
container = messageContainerManager.getContainer(destination.getPhysicalName());
this.subscription = createSubscription();
template.run(new Callback() {
public void execute() throws Throwable {
recover();
}
});
}
protected void recover() throws JMSException {
}
protected String getSubject() {
return getClass().getName() + "." + getName();
}
}