/*
* 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.servicemix.jbi.cluster.engine;
import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.io.File;
import java.net.ServerSocket;
import java.lang.management.ManagementFactory;
import javax.jms.ConnectionFactory;
import javax.xml.namespace.QName;
import javax.transaction.TransactionManager;
import junit.framework.TestCase;
import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.Service;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.pool.XaPooledConnectionFactory;
import org.apache.servicemix.nmr.api.NMR;
import org.apache.servicemix.nmr.api.Endpoint;
import org.apache.servicemix.nmr.api.Channel;
import org.apache.servicemix.nmr.api.Exchange;
import org.apache.servicemix.nmr.api.Status;
import org.apache.servicemix.nmr.api.Pattern;
import org.apache.servicemix.nmr.api.service.ServiceHelper;
import org.apache.servicemix.nmr.core.ServiceMix;
import org.apache.servicemix.nmr.core.ExchangeImpl;
import org.apache.servicemix.nmr.core.util.StringSource;
import org.apache.servicemix.jbi.runtime.impl.MessageExchangeImpl;
import org.apache.servicemix.jbi.runtime.impl.DeliveryChannelImpl;
import org.apache.servicemix.jbi.runtime.impl.AbstractComponentContext;
import org.apache.servicemix.jbi.runtime.ExchangeCompletedListener;
import org.apache.servicemix.jbi.cluster.requestor.Transacted;
import org.apache.servicemix.jbi.cluster.requestor.GenericJmsRequestorPool;
import org.apache.servicemix.jbi.cluster.requestor.AbstractPollingRequestorPool;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
import org.jencks.GeronimoPlatformTransactionManager;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.beans.factory.DisposableBean;
public abstract class AbstractClusterEndpointTest extends AutoFailTestSupport {
public static final String PROXY_ENDPOINT_NAME = "proxy";
public static final String RECEIVER_ENDPOINT_NAME = "receiver";
private final Log LOG = LogFactory.getLog(getClass());
protected NMR nmr1;
protected NMR nmr2;
protected Service broker;
protected ConnectionFactory connectionFactory;
protected ExchangeCompletedListener listener;
protected TransactionManager transactionManager;
protected TaskExecutor executor;
protected int port;
@Override
protected void setUp() throws Exception {
LOG.info("============================================================");
LOG.info(" Starting test: " + this.toString());
LOG.info("============================================================");
super.setUp();
ExchangeImpl.getConverter();
this.port = findFreePort();
this.executor = createTaskExecutor();
this.transactionManager = new GeronimoPlatformTransactionManager();
this.broker = createBroker(true);
this.connectionFactory = createConnectionFactory();
this.nmr1 = createNmr();
this.nmr2 = createNmr();
this.listener = new ExchangeCompletedListener(60000);
this.nmr1.getListenerRegistry().register(this.listener, null);
this.nmr2.getListenerRegistry().register(this.listener, null);
}
protected int findFreePort() {
int port = 61616;
try {
ServerSocket ss = new ServerSocket(0);
port = ss.getLocalPort();
ss.close();
} catch (Exception e) {
}
return port;
}
protected ConnectionFactory createConnectionFactory() {
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:" + port);
XaPooledConnectionFactory cnf = new XaPooledConnectionFactory(cf);
cnf.setTransactionManager(transactionManager);
return cnf;
}
@Override
protected void tearDown() throws Exception {
listener.assertExchangeCompleted();
((DisposableBean) executor).destroy();
if (executor instanceof DisposableBean) {
((DisposableBean) executor).destroy();
}
if (connectionFactory instanceof Service) {
((Service) connectionFactory).stop();
}
if (broker != null) {
broker.stop();
}
super.tearDown();
}
protected TaskExecutor createTaskExecutor() {
ThreadPoolTaskExecutor exec = new CleanThreadPoolTaskExecutor();
exec.setWaitForTasksToCompleteOnShutdown(true);
exec.setQueueCapacity(0);
exec.afterPropertiesSet();
return exec;
}
protected Service createBroker(boolean deleteData) throws Exception {
File data = new File("target/activemq");
if (deleteData) {
deleteFile(data);
}
BrokerService broker = new BrokerService();
broker.setPersistent(false);
broker.setUseJmx(true);
broker.addConnector("tcp://localhost:" + port);
broker.start();
return broker;
}
protected NMR createNmr() throws Exception {
ServiceMix nmr = new ServiceMix();
nmr.init();
return nmr;
}
protected ClusterEngine createCluster(NMR nmr, String name, Transacted transacted, boolean rollbackOnErrors) throws Exception {
ClusterEngine cluster = new ClusterEngine();
AbstractPollingRequestorPool pool = createPool();
pool.setDestinationName("destination");
pool.setConnectionFactory(connectionFactory);
pool.setTransactionManager(transactionManager);
pool.setTransacted(transacted);
pool.setAutoStartup(false);
pool.setTaskExecutor(executor);
pool.afterPropertiesSet();
cluster.setPool(pool);
cluster.setName(name);
cluster.setRollbackOnErrors(rollbackOnErrors);
nmr.getEndpointRegistry().register(cluster,
ServiceHelper.createMap(Endpoint.NAME, name));
nmr.getListenerRegistry().register(cluster, null);
return cluster;
}
protected AbstractPollingRequestorPool createPool() {
return new GenericJmsRequestorPool();
}
protected ProxyEndpoint createProxy(NMR nmr, ClusterEngine cluster) throws Exception {
ProxyEndpoint proxy = new ProxyEndpoint(QName.valueOf("{urn:test}receiver"));
nmr.getEndpointRegistry().register(proxy,
ServiceHelper.createMap(Endpoint.NAME, PROXY_ENDPOINT_NAME,
Endpoint.SERVICE_NAME, "{urn:test}proxy",
Endpoint.ENDPOINT_NAME, "endpoint",
AbstractComponentContext.INTERNAL_ENDPOINT, "true"));
SimpleClusterRegistration reg = new SimpleClusterRegistration();
reg.setEndpoint(proxy);
reg.init();
cluster.register(reg, null);
return proxy;
}
protected ReceiverEndpoint createReceiver(NMR nmr, boolean fault, boolean error) throws Exception {
ReceiverEndpoint receiver = new ReceiverEndpoint(fault, error);
nmr.getEndpointRegistry().register(receiver,
ServiceHelper.createMap(Endpoint.NAME, RECEIVER_ENDPOINT_NAME,
Endpoint.SERVICE_NAME, "{urn:test}receiver",
Endpoint.ENDPOINT_NAME, "endpoint",
AbstractComponentContext.INTERNAL_ENDPOINT, "true"));
return receiver;
}
public static class ProxyEndpoint implements Endpoint {
private final QName service;
private Channel channel;
public ProxyEndpoint(QName service) {
this.service = service;
}
public void setChannel(Channel channel) {
this.channel = channel;
}
public void process(Exchange exchange) {
@SuppressWarnings("unchecked")
Holder<Exchange> holder = (Holder<Exchange>) exchange.getProperty("correlated");
Exchange correlated = holder != null ? holder.get() : null;
if (exchange.getStatus() == Status.Error) {
correlated.setError(exchange.getError());
correlated.setStatus(Status.Error);
} else if (exchange.getStatus() == Status.Done) {
correlated.setStatus(Status.Done);
} else if (exchange.getFault(false) != null) {
correlated.setFault(exchange.getFault());
} else if (exchange.getOut(false) != null) {
correlated.setOut(exchange.getOut());
} else if (exchange.getIn(false) != null) {
correlated = channel.createExchange(exchange.getPattern());
exchange.setProperty("correlated", new Holder<Exchange>(correlated));
correlated.setProperty("correlated", new Holder<Exchange>(exchange));
//correlated.setProperty(JbiConstants.SENDER_ENDPOINT, getService() + ":" + getEndpoint());
correlated.setProperty(MessageExchangeImpl.SERVICE_NAME_PROP, service);
DeliveryChannelImpl.createTarget(channel.getNMR(), correlated);
correlated.setIn(exchange.getIn());
} else {
throw new IllegalStateException();
}
channel.send(correlated);
}
}
public static class Holder<T> {
private final T t;
public Holder(T t) {
this.t = t;
}
public T get() {
return t;
}
}
public static class ReceiverEndpoint implements Endpoint {
private final List<Exchange> exchanges = new LinkedList<Exchange>();
private final boolean sendFault;
private final boolean sendError;
private Map<String, Boolean> faultSent = new ConcurrentHashMap<String, Boolean>();
private Map<String, Boolean> errorSent = new ConcurrentHashMap<String, Boolean>();
private Channel channel;
public ReceiverEndpoint(boolean sendFault, boolean sendError) {
this.sendFault = sendFault;
this.sendError = sendError;
}
public List<Exchange> getExchanges() {
return exchanges;
}
public void setChannel(Channel channel) {
this.channel = channel;
}
public synchronized void process(Exchange exchange) {
synchronized (exchanges) {
exchanges.add(exchange.copy());
exchanges.notifyAll();
}
if (exchange.getStatus() == Status.Active) {
String key = exchange.getIn().getBody(String.class);
if (sendFault && key != null && !faultSent.containsKey(key)) {
exchange.getFault().setBody(new StringSource("<fault/>"));
channel.send(exchange);
faultSent.put(key, true);
} else if (sendError && key != null && !errorSent.containsKey(key)) {
exchange.setError(new Exception("error"));
exchange.setStatus(Status.Error);
channel.send(exchange);
errorSent.put(key, true);
} else if (exchange.getPattern() == Pattern.InOut || exchange.getPattern() == Pattern.InOptionalOut) {
exchange.getOut().setBody(new StringSource("<out/>"));
channel.send(exchange);
} else {
exchange.setStatus(Status.Done);
channel.send(exchange);
}
}
}
public void assertExchangesReceived(int count, long timeout) {
synchronized (exchanges) {
long cur = System.currentTimeMillis();
long end = cur + timeout;
while (cur < end) {
try {
if (exchanges.size() >= count) {
break;
}
exchanges.wait(end - cur);
} catch (InterruptedException e) {
e.printStackTrace();
}
cur = System.currentTimeMillis();
}
assertTrue("expected number of messages when received: " + exchanges.size(), count <= exchanges.size());
}
}
}
public static class CleanThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private long shutdownTimeout = 5 * 60 * 1000;
private boolean waitForTasksToCompleteOnShutdown = false;
public long getShutdownTimeout() {
return shutdownTimeout;
}
public void setShutdownTimeout(long shutdownTimeout) {
this.shutdownTimeout = shutdownTimeout;
}
@Override
public void setWaitForTasksToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) {
this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown;
super.setWaitForTasksToCompleteOnShutdown(waitForJobsToCompleteOnShutdown);
}
@Override
public void shutdown() {
super.shutdown();
if (waitForTasksToCompleteOnShutdown) {
try {
getThreadPoolExecutor().awaitTermination(shutdownTimeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
protected static boolean deleteFile(File fileToDelete) {
if (fileToDelete == null || !fileToDelete.exists()) {
return true;
}
boolean result = true;
if (fileToDelete.isDirectory()) {
File[] files = fileToDelete.listFiles();
if (files == null) {
result = false;
} else {
for (int i = 0; i < files.length; i++) {
File file = files[i];
if (file.getName().equals(".") || file.getName().equals("..")) {
continue;
}
if (file.isDirectory()) {
result &= deleteFile(file);
} else {
result &= file.delete();
}
}
}
}
result &= fileToDelete.delete();
return result;
}
}