/*
* 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.config.EjbModule;
import org.apache.openejb.jee.EjbJar;
import org.apache.openejb.jee.oejb3.EjbDeployment;
import org.apache.openejb.jee.oejb3.OpenejbJar;
import org.apache.openejb.junit.ApplicationComposer;
import org.apache.openejb.monitoring.LocalMBeanServer;
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.junit.Test;
import org.junit.runner.RunWith;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.management.ObjectName;
import java.util.Properties;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(ApplicationComposer.class)
public class ActiveMQResourceAdapterControlTest {
@Resource(name = "ActiveMQResourceAdapterControlTest/test/ejb/Mdb")
private Queue queue;
@Resource
private ConnectionFactory connectionFactory;
@Configuration
public Properties config() {
return new PropertiesBuilder()
.p("ra", "new://Resource?type=ActiveMQResourceAdapter")
.p("ra.brokerXmlConfig", "broker:(vm://localhost)?useJmx=false&persistent=false")
.p("ra.serverUrl", "vm://localhost")
.p("mdb", "new://Container?type=MESSAGE")
.p("mdb.resourceAdapter", "ra")
.p("mdb.InstanceLimit", "1")
.p("cf", "new://Resource?type=javax.jms.ConnectionFactory")
.p("cf.resourceAdapter", "ra")
.p("openejb.deploymentId.format", "{appId}/{ejbJarId}/{ejbName}")
.build();
}
@Module
@Classes(value = Mdb.class)
public EjbModule app() {
return new EjbModule(new EjbJar("test"), new OpenejbJar() {{
setId("test");
getEjbDeployment().add(new EjbDeployment() {{
setEjbName("ejb/Mdb");
getProperties().put("MdbActiveOnStartup", "false");
getProperties().put("MdbJMXControl", "default:type=test");
}});
}});
}
@Test
public void ensureControl() throws Exception {
assertFalse(Mdb.awaiter.message, sendAndWait("Will be received after", 10, TimeUnit.SECONDS));
setControl("start");
assertTrue(Mdb.awaiter.semaphore.tryAcquire(1, TimeUnit.MINUTES));
assertEquals("Will be received after", Mdb.awaiter.message);
final long start = System.currentTimeMillis();
assertTrue(sendAndWait("First", 1, TimeUnit.MINUTES));
assertEquals("First", Mdb.awaiter.message);
final long end = System.currentTimeMillis();
Mdb.awaiter.message = null;
setControl("stop");
// default would be wait 10s, but if machine is slow we compute it from the first msg stats
final long waitWithoutResponse = Math.max(10, 5 * (end - start) / 1000);
System.out.println("We'll wait " + waitWithoutResponse + "s to get a message on a stopped listener");
assertFalse(Mdb.awaiter.message, sendAndWait("Will be received after", waitWithoutResponse, TimeUnit.SECONDS));
assertNull(Mdb.awaiter.message);
setControl("start");
assertTrue(sendAndWait("Second", 1, TimeUnit.MINUTES));
assertEquals("Will be received after", Mdb.awaiter.message);
Mdb.awaiter.message = null;
assertTrue(Mdb.awaiter.semaphore.tryAcquire(1, TimeUnit.MINUTES));
assertEquals("Second", Mdb.awaiter.message);
}
private void setControl(final String action) throws Exception {
LocalMBeanServer.get().invoke(
new ObjectName("default:type=test"),
action, new Object[0], new String[0]);
}
private boolean sendAndWait(final String second, final long wait, final TimeUnit unit) throws JMSException {
doSend(second);
try {
return Mdb.awaiter.semaphore.tryAcquire(wait, unit);
} catch (final InterruptedException e) {
Thread.interrupted();
fail();
return false;
}
}
private void doSend(final String txt) throws JMSException {
Connection c = null;
try {
c = connectionFactory.createConnection();
Session session = null;
try {
session = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = null;
try {
producer = session.createProducer(queue);
producer.send(session.createTextMessage(txt));
} finally {
if (producer != null) {
producer.close();
}
}
} finally {
if (session != null) {
session.close();
}
}
} finally {
if (c != null) {
c.close();
}
}
}
@MessageDriven(name = "ejb/Mdb", activationConfig = {
@ActivationConfigProperty(propertyName = "maxSessions", propertyValue = "1"),
@ActivationConfigProperty(propertyName = "maxMessagesPerBatch", propertyValue = "1")
})
public static class Mdb implements MessageListener {
static final MessageAwaiter awaiter = new MessageAwaiter();
@Override
public synchronized void onMessage(final Message message) {
try {
awaiter.message = TextMessage.class.cast(message).getText();
} catch (final JMSException e) {
throw new IllegalStateException(e);
} finally {
awaiter.semaphore.release();
}
}
}
public static class MessageAwaiter {
private final Semaphore semaphore = new Semaphore(0);
private volatile String message;
}
}