/**
*
* 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.openejb.resource.activemq;
import org.apache.openejb.jee.WebApp;
import org.apache.openejb.junit.DeployApplication;
import org.apache.openejb.testing.ApplicationComposers;
import org.apache.openejb.testing.Classes;
import org.apache.openejb.testing.Configuration;
import org.apache.openejb.testing.Module;
import org.apache.openejb.testng.PropertiesBuilder;
import org.apache.openejb.util.Join;
import org.apache.openejb.util.NetworkUtil;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runners.model.Statement;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import java.util.Locale;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
// inspired from MessagingBeanTest in examples
public class ProperConnectionShutdownTest {
@Test
@Ignore("https://issues.apache.org/jira/browse/AMQ-6051")
public void run() throws Throwable {
final Thread[] threadsBefore = listThreads();
final AtomicReference<Thread[]> threadWhile = new AtomicReference<>();
// run test
final Statement testInContainer = new Statement() {
@Override
public void evaluate() throws Throwable {
messages.sendMessage("Hello World!");
messages.sendMessage("How are you?");
threadWhile.set(listThreads());
messages.sendMessage("Still spinning?");
assertEquals(messages.receiveMessage(), "Hello World!");
assertEquals(messages.receiveMessage(), "How are you?");
assertEquals(messages.receiveMessage(), "Still spinning?");
/* TODO: activate it when AMQ-6051 is fixed
// all worked, now hold a connection
new Thread(new Runnable() { // not daemon!
@Override
public void run() {
messages.blockConnection(); // oops, I forgot to close it
}
}).start();
*/
}
};
new DeployApplication(this, testInContainer, new ApplicationComposers(this)).evaluate();
Thread.sleep(2250); // AMQ state (started) polling for transport thread is 1s
while (Join.join("", listThreads()).contains("ActiveMQ Session Task")) { // let few sec to AMQ to leave the holding task
Thread.sleep(1000);
}
// ensure no connection are leaking
final Thread[] threadsAfter = listThreads();
int countAMQ = 0;
int countOthers = 0;
for (final Thread t : threadsAfter) {
if (!t.isAlive()) {
continue;
}
if (t.getName().contains("AMQ") || t.getName().toLowerCase(Locale.ENGLISH).contains("activemq")) {
countAMQ++;
} else {
countOthers++;
}
}
final String debugMessage = Join.join(", ", threadsAfter);
assertEquals(debugMessage, 0, countAMQ);
// geronimo libs spawn 2 threads we know: PoolIdleReleaseTimer and CurrentTime so we can get initial + 2 threads there
assertTrue(debugMessage, countOthers <= threadsBefore.length + 2);
}
private Thread[] listThreads() {
final Thread[] threads = new Thread[Thread.activeCount()];
final int count = Thread.enumerate(threads);
if (count < threads.length) {
final Thread[] copy = new Thread[count];
System.arraycopy(threads, 0, copy, 0, count);
return copy;
}
return threads;
}
@EJB
private Messages messages;
@Configuration
public Properties config() {
return new PropertiesBuilder()
.p("Default JMS Resource Adapter.BrokerXmlConfig", "broker:(tcp://localhost:" + NetworkUtil.getNextAvailablePort() + ")?useJmx=false")
.build();
}
@Module
@Classes(innerClassesAsBean = true)
public WebApp app() {
return new WebApp();
}
@Stateless
public static class Messages {
@Resource
private ConnectionFactory connectionFactory;
@Resource
private Queue chatQueue;
public void sendMessage(String text) throws JMSException {
Connection connection = null;
Session session = null;
try {
connection = connectionFactory.createConnection();
connection.start();
// Create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create a MessageProducer from the Session to the Topic or Queue
MessageProducer producer = session.createProducer(chatQueue);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
// Create a message
TextMessage message = session.createTextMessage(text);
// Tell the producer to send the message
producer.send(message);
} finally {
// Clean up
if (session != null) {
session.close();
}
if (connection != null) {
connection.close();
}
}
}
public String receiveMessage() throws JMSException {
Connection connection = null;
Session session = null;
MessageConsumer consumer = null;
try {
connection = connectionFactory.createConnection();
connection.start();
// Create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create a MessageConsumer from the Session to the Topic or Queue
consumer = session.createConsumer(chatQueue);
// Wait for a message
TextMessage message = (TextMessage) consumer.receive(1000);
return message.getText();
} finally {
if (consumer != null) {
consumer.close();
}
if (session != null) {
session.close();
}
if (connection != null) {
connection.close();
}
}
}
public void blockConnection() {
try {
connectionFactory.createConnection();
} catch (final JMSException e) {
throw new IllegalStateException(e);
}
}
}
}