/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.activemq.artemis.tests.integration.jms.jms2client;
import javax.jms.BytesMessage;
import javax.jms.CompletionListener;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.IllegalStateRuntimeException;
import javax.jms.InvalidDestinationRuntimeException;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSProducer;
import javax.jms.JMSRuntimeException;
import javax.jms.Message;
import javax.jms.MessageFormatRuntimeException;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.StreamMessage;
import javax.jms.TextMessage;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.tests.util.JMSTestBase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class JmsContextTest extends JMSTestBase {
private JMSContext context;
private final Random random = new Random();
private Queue queue1;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
context = createContext();
queue1 = createQueue(JmsContextTest.class.getSimpleName() + "Queue");
}
@Test
public void testCreateContext() {
Assert.assertNotNull(context);
}
@Test
public void testRollbackTest() {
JMSContext ctx = addContext(cf.createContext(JMSContext.SESSION_TRANSACTED));
JMSProducer producer = ctx.createProducer();
JMSConsumer cons = ctx.createConsumer(queue1);
producer.send(queue1, context.createTextMessage("hello"));
ctx.rollback();
assertNull(cons.receiveNoWait());
producer.send(queue1, context.createTextMessage("hello"));
ctx.commit();
assertNotNull(cons.receiveNoWait());
ctx.commit();
ctx.rollback();
assertNull(cons.receiveNoWait());
cons.close();
}
@Test
public void testDupsOK() {
JMSContext ctx = addContext(cf.createContext(JMSContext.DUPS_OK_ACKNOWLEDGE));
assertEquals(JMSContext.DUPS_OK_ACKNOWLEDGE, ctx.getSessionMode());
ctx.close();
ctx = addContext(cf.createContext(JMSContext.SESSION_TRANSACTED));
assertEquals(JMSContext.SESSION_TRANSACTED, ctx.getSessionMode());
ctx.close();
ctx = addContext(cf.createContext(JMSContext.CLIENT_ACKNOWLEDGE));
assertEquals(JMSContext.CLIENT_ACKNOWLEDGE, ctx.getSessionMode());
ctx.close();
ctx = addContext(cf.createContext(JMSContext.AUTO_ACKNOWLEDGE));
assertEquals(JMSContext.AUTO_ACKNOWLEDGE, ctx.getSessionMode());
}
@Test
public void testReceiveBytes() throws Exception {
JMSProducer producer = context.createProducer();
JMSConsumer consumer = context.createConsumer(queue1);
BytesMessage bytesSend = context.createBytesMessage();
bytesSend.writeByte((byte) 1);
bytesSend.writeLong(2L);
producer.send(queue1, bytesSend);
BytesMessage msgReceived = (BytesMessage) consumer.receiveNoWait();
byte[] bytesArray = msgReceived.getBody(byte[].class);
assertEquals((byte) 1, msgReceived.readByte());
assertEquals(2L, msgReceived.readLong());
DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(bytesArray));
assertEquals((byte) 1, dataInputStream.readByte());
assertEquals(2L, dataInputStream.readLong());
}
@Test
public void testReceiveText() throws Exception {
JMSProducer producer = context.createProducer();
JMSConsumer consumer = context.createConsumer(queue1);
String randomStr = newXID().toString();
System.out.println("RandomStr:" + randomStr);
TextMessage sendMsg = context.createTextMessage(randomStr);
producer.send(queue1, sendMsg);
TextMessage receiveMsg = (TextMessage) consumer.receiveNoWait();
assertEquals(randomStr, receiveMsg.getText());
}
@Test
public void testDelay() throws Exception {
JMSProducer producer = context.createProducer();
JMSConsumer consumer = context.createConsumer(queue1);
producer.setDeliveryDelay(500);
long timeStart = System.currentTimeMillis();
String strRandom = newXID().toString();
producer.send(queue1, context.createTextMessage(strRandom));
TextMessage msg = (TextMessage) consumer.receive(2500);
assertNotNull(msg);
long actualDelay = System.currentTimeMillis() - timeStart;
assertTrue("delay is not working, actualDelay=" + actualDelay, actualDelay >= 500 && actualDelay < 2000);
assertEquals(strRandom, msg.getText());
}
@Test
public void testExpire() throws Exception {
JMSProducer producer = context.createProducer();
producer.setTimeToLive(500);
String strRandom = newXID().toString();
producer.send(queue1, context.createTextMessage(strRandom));
Thread.sleep(700);
// Create consumer after message is expired, making it to expire at the server's
JMSConsumer consumer = context.createConsumer(queue1);
TextMessage msg = (TextMessage) consumer.receiveNoWait();
// Time to live kicked in, so it's supposed to return null
assertNull(msg);
strRandom = newXID().toString();
producer.send(queue1, context.createTextMessage(strRandom));
Thread.sleep(700);
// Receive second message, expiring on client
msg = (TextMessage) consumer.receiveNoWait();
assertNull(msg);
strRandom = newXID().toString();
producer.send(queue1, context.createTextMessage(strRandom));
// will receive a message that's not expired now
msg = (TextMessage) consumer.receiveNoWait();
assertNotNull(msg);
assertEquals(strRandom, msg.getText());
}
@Test
public void testDeliveryMode() throws Exception {
JMSProducer producer = context.createProducer();
JMSConsumer consumer = context.createConsumer(queue1);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
String strRandom = newXID().toString();
producer.send(queue1, context.createTextMessage(strRandom));
TextMessage msg = (TextMessage) consumer.receiveNoWait();
assertNotNull(msg);
assertEquals(DeliveryMode.NON_PERSISTENT, msg.getJMSDeliveryMode());
}
@Test
public void testInvalidMessage() {
JMSProducer producer = context.createProducer();
try {
producer.send(queue1, (Message) null);
Assert.fail("null msg");
} catch (MessageFormatRuntimeException expected) {
// no-op
}
}
@Test
public void testInvalidDestination() {
JMSProducer producer = context.createProducer();
Message msg = context.createMessage();
try {
producer.send((Destination) null, msg);
Assert.fail("null Destination");
} catch (InvalidDestinationRuntimeException expected) {
// no-op
}
}
@Test
public void testSendStreamMessage() throws JMSException, InterruptedException {
JmsProducerCompletionListenerTest.CountingCompletionListener cl = new JmsProducerCompletionListenerTest.CountingCompletionListener(1);
JMSProducer producer = context.createProducer();
producer.setAsync(cl);
StreamMessage msg = context.createStreamMessage();
msg.setStringProperty("name", name.getMethodName());
String bprop = "booleanProp";
String iprop = "intProp";
msg.setBooleanProperty(bprop, true);
msg.setIntProperty(iprop, 42);
msg.writeBoolean(true);
msg.writeInt(67);
producer.send(queue1, msg);
JMSConsumer consumer = context.createConsumer(queue1);
Message msg2 = consumer.receive(100);
Assert.assertNotNull(msg2);
Assert.assertTrue(cl.completionLatch.await(1, TimeUnit.SECONDS));
StreamMessage sm = (StreamMessage) cl.lastMessage;
Assert.assertEquals(true, sm.getBooleanProperty(bprop));
Assert.assertEquals(42, sm.getIntProperty(iprop));
Assert.assertEquals(true, sm.readBoolean());
Assert.assertEquals(67, sm.readInt());
}
@Test
public void testSetClientIdLate() {
JMSProducer producer = context.createProducer();
Message msg = context.createMessage();
producer.send(queue1, msg);
try {
context.setClientID("id");
Assert.fail("expected exception");
} catch (IllegalStateRuntimeException e) {
// no op
}
}
@Test
public void testCloseSecondContextConnectionRemainsOpen() throws JMSException {
JMSContext localContext = context.createContext(JMSContext.CLIENT_ACKNOWLEDGE);
Assert.assertEquals("client_ack", JMSContext.CLIENT_ACKNOWLEDGE, localContext.getSessionMode());
JMSProducer producer = localContext.createProducer();
JMSConsumer consumer = localContext.createConsumer(queue1);
final int pass = 1;
for (int idx = 0; idx < 2; idx++) {
Message m = localContext.createMessage();
int intProperty = random.nextInt();
m.setIntProperty("random", intProperty);
Assert.assertNotNull(m);
producer.send(queue1, m);
m = null;
Message msg = consumer.receive(100);
Assert.assertNotNull("must have a msg", msg);
Assert.assertEquals(intProperty, msg.getIntProperty("random"));
/* In the second pass we close the connection before ack'ing */
if (idx == pass) {
localContext.close();
}
/**
* From {@code JMSContext.close()}'s javadoc:<br/>
* Invoking the {@code acknowledge} method of a received message from a closed connection's
* session must throw an {@code IllegalStateRuntimeException}. Closing a closed connection
* must NOT throw an exception.
*/
try {
msg.acknowledge();
Assert.assertEquals("connection should be open on pass 0. It is " + pass, 0, idx);
} catch (javax.jms.IllegalStateException expected) {
// HORNETQ-1209 "JMS 2.0" XXX JMSContext javadoc says we must expect a
// IllegalStateRuntimeException here. But Message.ack...() says it must throws the
// non-runtime variant.
Assert.assertEquals("we only close the connection on pass " + pass, pass, idx);
}
}
}
@Test(expected = JMSRuntimeException.class)
public void testInvalidSessionModesValueMinusOne() {
context.createContext(-1);
}
@Test(expected = JMSRuntimeException.class)
public void testInvalidSessionModesValue4() {
context.createContext(4);
}
@Test
public void testGetAnotherContextFromIt() {
JMSContext c2 = context.createContext(Session.DUPS_OK_ACKNOWLEDGE);
Assert.assertNotNull(c2);
Assert.assertEquals(Session.DUPS_OK_ACKNOWLEDGE, c2.getSessionMode());
Message m2 = c2.createMessage();
Assert.assertNotNull(m2);
c2.close(); // should close its session, but not its (shared) connection
try {
c2.createMessage();
Assert.fail("session should be closed...");
} catch (JMSRuntimeException expected) {
// expected
}
Message m1 = context.createMessage();
Assert.assertNotNull("connection must be open", m1);
}
@Test
public void testSetGetClientIdNewContext() {
final String id = "123";
JMSContext c = context;// createContext();
c.setClientID(id);
JMSContext c2 = addContext(c.createContext(Session.CLIENT_ACKNOWLEDGE));
Assert.assertEquals(id, c2.getClientID());
}
@Test
public void testGetClientId() {
JMSContext context2 = addContext(context.createContext(Session.AUTO_ACKNOWLEDGE));
final String id = "ID: " + random.nextInt();
context.setClientID(id);
Assert.assertEquals("id's must match because the connection is shared", id, context2.getClientID());
}
@Test
public void testCreateConsumerWithSelector() throws JMSException {
final String filterName = "magicIndexMessage";
final int total = 5;
JMSProducer producer = context.createProducer();
JMSConsumer consumerNoSelect = context.createConsumer(queue1);
JMSConsumer consumer = context.createConsumer(queue1, filterName + "=TRUE");
for (int i = 0; i < total; i++) {
Message msg = context.createTextMessage("message " + i);
msg.setBooleanProperty(filterName, i == 3);
producer.send(queue1, msg);
}
Message msg0 = consumer.receive(500);
Assert.assertNotNull(msg0);
msg0.acknowledge();
Assert.assertNull("no more messages", consumer.receiveNoWait());
for (int i = 0; i < total - 1; i++) {
Message msg = consumerNoSelect.receive(100);
Assert.assertNotNull(msg);
msg.acknowledge();
}
Assert.assertNull("no more messages", consumerNoSelect.receiveNoWait());
}
@Test
public void testContextStopAndCloseFromMessageListeners() throws Exception {
final JMSContext context1 = context.createContext(Session.AUTO_ACKNOWLEDGE);
JMSConsumer consumer1 = context1.createConsumer(queue1);
final CountDownLatch latch1 = new CountDownLatch(1);
InvalidMessageListener listener1 = new InvalidMessageListener(context1, latch1, 1);
consumer1.setMessageListener(listener1);
JMSProducer producer = context1.createProducer();
Message msg = context1.createTextMessage("first message");
producer.send(queue1, msg);
latch1.await();
Throwable error1 = listener1.getError();
assertNotNull(error1);
assertTrue(error1 instanceof IllegalStateRuntimeException);
context1.close();
final JMSContext context2 = context.createContext(Session.AUTO_ACKNOWLEDGE);
JMSConsumer consumer2 = context2.createConsumer(queue1);
final CountDownLatch latch2 = new CountDownLatch(1);
InvalidMessageListener listener2 = new InvalidMessageListener(context2, latch2, 2);
consumer2.setMessageListener(listener2);
JMSProducer producer2 = context2.createProducer();
Message msg2 = context2.createTextMessage("second message");
producer2.send(queue1, msg2);
latch2.await();
Throwable error2 = listener2.getError();
assertNotNull(error2);
assertTrue(error2 instanceof IllegalStateRuntimeException);
context2.close();
}
@Test
public void recoverAckTest() throws Exception {
// Create JMSContext with CLIENT_ACKNOWLEDGE
try (JMSContext context = cf.createContext(JMSContext.CLIENT_ACKNOWLEDGE)) {
int numMessages = 10;
TextMessage textMessage = null;
// Create JMSConsumer from JMSContext
JMSConsumer consumer = context.createConsumer(queue1);
// Create JMSProducer from JMSContext
JMSProducer producer = context.createProducer();
// send messages
for (int i = 0; i < numMessages; i++) {
String message = "text message " + i;
textMessage = context.createTextMessage(message);
textMessage.setStringProperty("COM_SUN_JMS_TESTNAME", "recoverAckTest" + i);
producer.send(queue1, textMessage);
}
// receive messages but do not acknowledge
for (int i = 0; i < numMessages; i++) {
textMessage = (TextMessage) consumer.receive(5000);
assertNotNull(textMessage);
}
context.recover();
// receive messages a second time followed by acknowledge
for (int i = 0; i < numMessages; i++) {
textMessage = (TextMessage) consumer.receive(5000);
assertNotNull(textMessage);
}
// Acknowledge all messages
context.acknowledge();
}
// doing this check with another context / consumer to make sure it was acked.
try (JMSContext context = cf.createContext(JMSContext.CLIENT_ACKNOWLEDGE)) {
// Create JMSConsumer from JMSContext
JMSConsumer consumer = context.createConsumer(queue1);
assertNull(consumer.receiveNoWait());
}
}
@Test
public void bytesMessage() throws Exception {
context = cf.createContext();
try {
JMSProducer producer = context.createProducer();
BytesMessage bMsg = context.createBytesMessage();
bMsg.setStringProperty("COM_SUN_JMS_TESTNAME", "sendAndRecvMsgOfEachTypeCLTest");
bMsg.writeByte((byte) 1);
bMsg.writeInt(22);
CountDownLatch latch = new CountDownLatch(1);
SimpleCompletionListener listener = new SimpleCompletionListener(latch);
producer.setAsync(listener);
producer.send(queue1, bMsg);
assertTrue(latch.await(5, TimeUnit.SECONDS));
assertEquals(listener.message.readByte(), (byte) 1);
assertEquals(listener.message.readInt(), 22);
} finally {
context.close();
}
}
@Test
public void illegalStateRuntimeExceptionTests() throws Exception {
JMSProducer producer = context.createProducer();
JMSConsumer consumer = context.createConsumer(queue1);
System.out.println("Creating TextMessage");
TextMessage expTextMessage = context.createTextMessage("Call commit");
CountDownLatch latch = new CountDownLatch(1);
JMSCOntextStopCompletionListener listener = new JMSCOntextStopCompletionListener(context, latch);
producer.setAsync(listener);
producer.send(queue1, expTextMessage);
assertTrue(latch.await(5, TimeUnit.SECONDS));
assertNull(listener.ex);
}
private static class SimpleCompletionListener implements CompletionListener {
private CountDownLatch latch;
private BytesMessage message;
private SimpleCompletionListener(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void onCompletion(Message message) {
this.message = (BytesMessage) message;
latch.countDown();
}
@Override
public void onException(Message message, Exception exception) {
//To change body of implemented methods use File | Settings | File Templates.
}
}
private static class InvalidMessageListener implements MessageListener {
private int id;
private CountDownLatch latch;
private JMSContext context;
private volatile Throwable error;
private InvalidMessageListener(JMSContext context, CountDownLatch latch, int id) {
this.id = id;
this.latch = latch;
this.context = context;
}
public Throwable getError() {
return error;
}
@Override
public void onMessage(Message arg0) {
switch (id) {
case 1:
stopContext();
break;
case 2:
closeContext();
break;
default:
break;
}
latch.countDown();
}
private void stopContext() {
try {
context.stop();
} catch (Throwable t) {
error = t;
}
}
private void closeContext() {
try {
context.close();
} catch (Throwable t) {
error = t;
}
}
}
private class JMSCOntextStopCompletionListener implements CompletionListener {
private JMSContext context;
private CountDownLatch latch;
private Exception ex;
private JMSCOntextStopCompletionListener(JMSContext context, CountDownLatch latch) {
this.context = context;
this.latch = latch;
}
@Override
public void onCompletion(Message message) {
try {
context.stop();
} catch (Exception e) {
this.ex = e;
}
latch.countDown();
}
@Override
public void onException(Message message, Exception exception) {
}
}
}