001 /**
002 *
003 * Copyright 2004 Protique Ltd
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 **/
018 package org.activemq.transport.composite;
019
020 import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
021 import org.apache.commons.logging.Log;
022 import org.apache.commons.logging.LogFactory;
023 import org.activemq.TimeoutExpiredException;
024 import org.activemq.io.WireFormat;
025 import org.activemq.message.Packet;
026 import org.activemq.message.PacketListener;
027 import org.activemq.message.Receipt;
028 import org.activemq.message.ReceiptHolder;
029 import org.activemq.transport.TransportChannel;
030 import org.activemq.transport.TransportChannelProvider;
031 import org.activemq.transport.TransportChannelSupport;
032 import org.activemq.transport.TransportStatusEvent;
033 import org.activemq.transport.TransportStatusEventListener;
034
035 import javax.jms.ExceptionListener;
036 import javax.jms.JMSException;
037 import java.net.URI;
038 import java.util.ArrayList;
039 import java.util.Collections;
040 import java.util.List;
041
042 /**
043 * A Compsite implementation of a TransportChannel
044 *
045 * @version $Revision: 1.2 $
046 */
047 public class CompositeTransportChannel extends TransportChannelSupport implements TransportStatusEventListener {
048 private static final Log log = LogFactory.getLog(CompositeTransportChannel.class);
049
050 protected List uris;
051 protected URI currentURI;
052 protected TransportChannel channel;
053 protected SynchronizedBoolean closed;
054 protected SynchronizedBoolean started;
055 protected int maximumRetries = 10;
056 protected long failureSleepTime = 500L;
057 protected long establishConnectionTimeout = 30000L;
058 protected long maximumTimeout = 30000L;
059 protected boolean incrementTimeout = true;
060
061
062 public CompositeTransportChannel(WireFormat wireFormat) {
063 super(wireFormat);
064 this.uris = Collections.synchronizedList(new ArrayList());
065 closed = new SynchronizedBoolean(false);
066 started = new SynchronizedBoolean(false);
067 }
068
069 public CompositeTransportChannel(WireFormat wireFormat, List uris) {
070 this(wireFormat);
071 this.uris.addAll(uris);
072 }
073
074 public String toString() {
075 return "CompositeTransportChannel: " + channel;
076 }
077
078 public void start() throws JMSException {
079 if (started.commit(false, true)) {
080 // // Since we could take a LONG time to connect to one of the servers in the connection list,
081 // // do the connect async so that the client has a chance to stop() the channel if he needs to shutdown
082 // // before a connection is established.
083 // new Thread() {
084 // public void run() {
085 // try {
086 establishConnection(establishConnectionTimeout);
087 // } catch (JMSException e) {
088 // if(getExceptionListener()!=null) {
089 // getExceptionListener().onException(e);
090 // }
091 // }
092 fireStatusEvent(new TransportStatusEvent(CompositeTransportChannel.this,TransportStatusEvent.CONNECTED));
093 // }
094 // }.start();
095 }
096 }
097
098 /**
099 * close the channel
100 */
101 public void stop() {
102 if (closed.commit(false, true)) {
103 if (channel != null) {
104 try {
105 channel.stop();
106 }
107 catch (Exception e) {
108 log.warn("Caught while closing: " + e + ". Now Closed", e);
109 }
110 finally {
111 channel = null;
112 super.stop();
113 }
114 }
115 }
116 }
117
118 /**
119 * Forces disconnect by delegating to the child channel
120 */
121 public void forceDisconnect() {
122 if (channel != null) channel.forceDisconnect();
123 }
124
125 public Receipt send(Packet packet) throws JMSException {
126 return getChannel().send(packet);
127 }
128
129
130 public Receipt send(Packet packet, int timeout) throws JMSException {
131 return getChannel().send(packet, timeout);
132 }
133
134
135 public void asyncSend(Packet packet) throws JMSException {
136 getChannel().asyncSend(packet);
137 }
138
139 public ReceiptHolder asyncSendWithReceipt(Packet packet) throws JMSException {
140 return getChannel().asyncSendWithReceipt(packet);
141 }
142
143 public void setPacketListener(PacketListener listener) {
144 super.setPacketListener(listener);
145 if (channel != null) {
146 channel.setPacketListener(listener);
147 }
148 }
149
150 public void setExceptionListener(ExceptionListener listener) {
151 super.setExceptionListener(listener);
152 if (channel != null) {
153 channel.setExceptionListener(listener);
154 }
155 }
156
157
158 public boolean isMulticast() {
159 return false;
160 }
161
162 // Properties
163 //-------------------------------------------------------------------------
164
165
166 /**
167 * Return the maximum amount of time spent trying to establish a connection
168 * or a negative number to keep going forever
169 *
170 * @return
171 */
172 public long getEstablishConnectionTimeout() {
173 return establishConnectionTimeout;
174 }
175
176 public void setEstablishConnectionTimeout(long establishConnectionTimeout) {
177 this.establishConnectionTimeout = establishConnectionTimeout;
178 }
179
180 public int getMaximumRetries() {
181 return maximumRetries;
182 }
183
184 public void setMaximumRetries(int maximumRetries) {
185 this.maximumRetries = maximumRetries;
186 }
187
188 public long getFailureSleepTime() {
189 return failureSleepTime;
190 }
191
192 public void setFailureSleepTime(long failureSleepTime) {
193 this.failureSleepTime = failureSleepTime;
194 }
195
196 public List getUris() {
197 return uris;
198 }
199
200 public void setUris(List list) {
201 synchronized (uris) {
202 uris.clear();
203 uris.addAll(list);
204 }
205 }
206
207
208 /**
209 * @return Returns the incrementTimeout.
210 */
211 public boolean isIncrementTimeout() {
212 return incrementTimeout;
213 }
214 /**
215 * @param incrementTimeout The incrementTimeout to set.
216 */
217 public void setIncrementTimeout(boolean incrementTimeout) {
218 this.incrementTimeout = incrementTimeout;
219 }
220 /**
221 * @return Returns the maximumTimeout.
222 */
223 public long getMaximumTimeout() {
224 return maximumTimeout;
225 }
226 /**
227 * @param maximumTimeout The maximumTimeout to set.
228 */
229 public void setMaximumTimeout(long maximumTimeout) {
230 this.maximumTimeout = maximumTimeout;
231 }
232
233 /**
234 * Can this wireformat process packets of this version
235 * @param version the version number to test
236 * @return true if can accept the version
237 */
238 public boolean canProcessWireFormatVersion(int version){
239 return channel != null ? channel.canProcessWireFormatVersion(version) : true;
240 }
241
242 /**
243 * @return the current version of this wire format
244 */
245 public int getCurrentWireFormatVersion(){
246 return channel != null ? channel.getCurrentWireFormatVersion() : 1;
247 }
248
249 /**
250 * Access to the current channel if one is active
251 * @throws JMSException if no channel is available
252 */
253 public TransportChannel getChannel() throws JMSException {
254 if (channel == null) {
255 throw new JMSException("No TransportChannel connection available");
256 }
257 return channel;
258 }
259
260 // Implementation methods
261 //-------------------------------------------------------------------------
262
263 protected void establishConnection(long timeout) throws JMSException {
264 // lets try connect
265 boolean connected = false;
266 long time = failureSleepTime;
267 long startTime = System.currentTimeMillis();
268 for (int i = 0;!connected && (i < maximumRetries || maximumRetries <= 0) && !closed.get() && !isPendingStop();i++) {
269 List list = new ArrayList(getUris());
270 if (i > 0) {
271 if (maximumRetries > 0 || timeout > 0) {
272 long current = System.currentTimeMillis();
273 if (timeout >= 0) {
274 if (current + time > startTime + timeout) {
275 time = startTime + timeout - current;
276 }
277 }
278 if (current > startTime + timeout || time <= 0) {
279 throw new TimeoutExpiredException("Could not connect to any of the URIs: " + list);
280 }
281 }
282 log.info("Could not connect; sleeping for: " + time + " millis and trying again");
283 try {
284 Thread.sleep(time);
285 }
286 catch (InterruptedException e) {
287 log.warn("Sleep interupted: " + e, e);
288 }
289 if (incrementTimeout && time < maximumTimeout) {
290 time *= 2;
291 time = time > maximumTimeout ? maximumTimeout : time;
292 }
293 }
294 while (!connected && !list.isEmpty() && !closed.get() && !isPendingStop()) {
295 URI uri = extractURI(list);
296 try {
297 attemptToConnect(uri);
298 configureChannel();
299 connected = true;
300 currentURI = uri;
301 }
302 catch (JMSException e) {
303 log.info("Could not connect to: " + uri + ". Reason: " + e);
304 }
305 }
306 }
307 if (!connected && !closed.get()) {
308 StringBuffer buffer = new StringBuffer("");
309 Object[] uriArray = getUris().toArray();
310 for (int i = 0;i < uriArray.length;i++) {
311 buffer.append(uriArray[i]);
312 if (i < (uriArray.length - 1)) {
313 buffer.append(",");
314 }
315 }
316 JMSException jmsEx = new JMSException("Failed to connect to resource(s): " + buffer.toString());
317 throw jmsEx;
318 }
319 }
320
321
322 protected void configureChannel() {
323 ExceptionListener exceptionListener = getExceptionListener();
324 if (exceptionListener != null) {
325 channel.setExceptionListener(exceptionListener);
326 }
327 PacketListener packetListener = getPacketListener();
328 if (packetListener != null) {
329 channel.setPacketListener(packetListener);
330 }
331
332 channel.addTransportStatusEventListener(this);
333 channel.setCachingEnabled(isCachingEnabled());
334 channel.setNoDelay(isNoDelay());
335 channel.setUsedInternally(isUsedInternally());
336 }
337
338 protected URI extractURI(List list) throws JMSException {
339 int idx = 0;
340 if (list.size() > 1) {
341 do {
342 idx = (int) (Math.random() * list.size());
343 }
344 while (idx < 0 || idx >= list.size());
345 }
346 return (URI) list.remove(idx);
347 }
348
349 protected void attemptToConnect(URI uri) throws JMSException {
350 WireFormat wf = currentWireFormat.copy();
351 channel = TransportChannelProvider.create(wf, uri);
352 if (started.get()) {
353 channel.start();
354 }
355 }
356
357 public void statusChanged(TransportStatusEvent event) {
358 // Delegate to own listeners
359 //set the transport to 'this'
360 event.setTransportChannel(this);
361 fireStatusEvent(event);
362 }
363
364 public boolean isTransportConnected() {
365 return channel == null ? false : channel.isTransportConnected();
366 }
367
368 public long getLastReceiptTimestamp() {
369 return channel == null ? System.currentTimeMillis() : channel.getLastReceiptTimestamp();
370 }
371 }