/**
*
* 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.codehaus.activemq.transport.composite;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.activemq.TimeoutExpiredException;
import org.codehaus.activemq.io.WireFormat;
import org.codehaus.activemq.message.Packet;
import org.codehaus.activemq.message.PacketListener;
import org.codehaus.activemq.message.Receipt;
import org.codehaus.activemq.message.ReceiptHolder;
import org.codehaus.activemq.transport.TransportChannel;
import org.codehaus.activemq.transport.TransportChannelProvider;
import org.codehaus.activemq.transport.TransportChannelSupport;
import org.codehaus.activemq.transport.TransportStatusEvent;
import org.codehaus.activemq.transport.TransportStatusEventListener;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A Compsite implementation of a TransportChannel
*
* @version $Revision: 1.4 $
*/
public class CompositeTransportChannel extends TransportChannelSupport implements TransportStatusEventListener {
private static final Log log = LogFactory.getLog(CompositeTransportChannel.class);
protected WireFormat wireFormat;
protected List uris;
protected TransportChannel channel;
protected SynchronizedBoolean closed;
protected SynchronizedBoolean started;
protected int maximumRetries = 10;
protected long failureSleepTime = 500L;
protected URI currentURI;
private long establishConnectionTimeout = 30000L;
public CompositeTransportChannel(WireFormat wireFormat) {
this.wireFormat = wireFormat;
this.uris = Collections.synchronizedList(new ArrayList());
closed = new SynchronizedBoolean(false);
started = new SynchronizedBoolean(false);
}
public CompositeTransportChannel(WireFormat wireFormat, List uris) {
this(wireFormat);
this.uris.addAll(uris);
}
public String toString() {
return "CompositeTransportChannel: " + channel;
}
public void start() throws JMSException {
if (started.commit(false, true)) {
establishConnection(establishConnectionTimeout);
}
}
/**
* close the channel
*/
public void stop() {
if (closed.commit(false, true)) {
if (channel != null) {
try {
channel.stop();
}
catch (Exception e) {
log.warn("Caught while closing: " + e + ". Now Closed", e);
}
finally {
channel = null;
super.stop();
}
}
}
}
/**
* Forces disconnect by delegating to the child channel
*/
public void forceDisconnect() {
if (channel != null) channel.forceDisconnect();
}
public Receipt send(Packet packet) throws JMSException {
return getChannel().send(packet);
}
public Receipt send(Packet packet, int timeout) throws JMSException {
return getChannel().send(packet, timeout);
}
public void asyncSend(Packet packet) throws JMSException {
getChannel().asyncSend(packet);
}
public ReceiptHolder asyncSendWithReceipt(Packet packet) throws JMSException {
return getChannel().asyncSendWithReceipt(packet);
}
public void setPacketListener(PacketListener listener) {
super.setPacketListener(listener);
if (channel != null) {
channel.setPacketListener(listener);
}
}
public void setExceptionListener(ExceptionListener listener) {
super.setExceptionListener(listener);
if (channel != null) {
channel.setExceptionListener(listener);
}
}
public boolean isMulticast() {
return false;
}
// Properties
//-------------------------------------------------------------------------
/**
* Return the maximum amount of time spent trying to establish a connection
* or a negative number to keep going forever
*
* @return
*/
public long getEstablishConnectionTimeout() {
return establishConnectionTimeout;
}
public void setEstablishConnectionTimeout(long establishConnectionTimeout) {
this.establishConnectionTimeout = establishConnectionTimeout;
}
public int getMaximumRetries() {
return maximumRetries;
}
public void setMaximumRetries(int maximumRetries) {
this.maximumRetries = maximumRetries;
}
public long getFailureSleepTime() {
return failureSleepTime;
}
public void setFailureSleepTime(long failureSleepTime) {
this.failureSleepTime = failureSleepTime;
}
public List getUris() {
return uris;
}
public void setUris(List list) {
synchronized (uris) {
uris.clear();
uris.addAll(list);
}
}
/**
* Can this wireformat process packets of this version
* @param version the version number to test
* @return true if can accept the version
*/
public boolean canProcessWireFormatVersion(int version){
return channel != null ? channel.canProcessWireFormatVersion(version) : true;
}
/**
* @return the current version of this wire format
*/
public int getCurrentWireFormatVersion(){
return channel != null ? channel.getCurrentWireFormatVersion() : 1;
}
// Implementation methods
//-------------------------------------------------------------------------
protected void establishConnection(long timeout) throws JMSException {
// lets try connect
boolean connected = false;
long time = failureSleepTime;
long startTime = System.currentTimeMillis();
for (int i = 0; !connected && (i < maximumRetries || maximumRetries <= 0) && !closed.get() && !isPendingStop(); i++) {
List list = new ArrayList(getUris());
if (i > 0) {
if (maximumRetries > 0 || timeout > 0) {
long current = System.currentTimeMillis();
if (timeout >= 0) {
if (current + time > startTime + timeout) {
time = startTime + timeout - current;
}
}
if (current > startTime + timeout || time <= 0) {
throw new TimeoutExpiredException("Could not connect to any of the URIs: " + list);
}
}
log.info("Could not connect; sleeping for: " + time + " millis and trying again");
try {
Thread.sleep(time);
}
catch (InterruptedException e) {
log.warn("Sleep interupted: " + e, e);
}
if (maximumRetries > 0) {
time *= 2;
}
}
while (!connected && !list.isEmpty() && !closed.get() && !isPendingStop()) {
URI uri = extractURI(list);
try {
attemptToConnect(uri);
configureChannel();
connected = true;
currentURI = uri;
}
catch (JMSException e) {
log.info("Could not connect to: " + uri + ". Reason: " + e);
}
}
}
if (!connected && !closed.get()) {
StringBuffer buffer = new StringBuffer("");
Object[] uriArray = getUris().toArray();
for (int i = 0; i < uriArray.length; i++) {
buffer.append(uriArray[i]);
if (i < (uriArray.length - 1)) {
buffer.append(",");
}
}
JMSException jmsEx = new JMSException("Failed to connect to resource(s): " + buffer.toString());
throw jmsEx;
}
}
protected TransportChannel getChannel() throws JMSException {
if (channel == null) {
throw new JMSException("No TransportChannel connection available");
}
return channel;
}
protected void configureChannel() {
ExceptionListener exceptionListener = getExceptionListener();
if (exceptionListener != null) {
channel.setExceptionListener(exceptionListener);
}
PacketListener packetListener = getPacketListener();
if (packetListener != null) {
channel.setPacketListener(packetListener);
}
channel.addTransportStatusEventListener(this);
}
protected URI extractURI(List list) throws JMSException {
int idx = 0;
if (list.size() > 1) {
do {
idx = (int) (Math.random() * list.size());
}
while (idx < 0 || idx >= list.size());
}
return (URI) list.remove(idx);
}
protected void attemptToConnect(URI uri) throws JMSException {
channel = TransportChannelProvider.create(wireFormat, uri);
if (started.get()) {
channel.start();
}
}
public void statusChanged(TransportStatusEvent event) {
// Delegate to own listeners
fireStatusEvent(event);
}
public boolean isTransportConnected() {
return channel == null ? false : channel.isTransportConnected();
}
}