PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/tomcat-7.0.2/java/org/apache/catalina/tribes/group/GroupChannel.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 684 lines | 361 code | 63 blank | 260 comment | 76 complexity | 379eb864b6301cf520ca04d3a7a656ee MD5 | raw file
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package org.apache.catalina.tribes.group;
  18. import java.io.Serializable;
  19. import java.util.ArrayList;
  20. import java.util.Iterator;
  21. import org.apache.catalina.tribes.ByteMessage;
  22. import org.apache.catalina.tribes.Channel;
  23. import org.apache.catalina.tribes.ChannelException;
  24. import org.apache.catalina.tribes.ChannelInterceptor;
  25. import org.apache.catalina.tribes.ChannelListener;
  26. import org.apache.catalina.tribes.ChannelMessage;
  27. import org.apache.catalina.tribes.ChannelReceiver;
  28. import org.apache.catalina.tribes.ChannelSender;
  29. import org.apache.catalina.tribes.ErrorHandler;
  30. import org.apache.catalina.tribes.ManagedChannel;
  31. import org.apache.catalina.tribes.Member;
  32. import org.apache.catalina.tribes.MembershipListener;
  33. import org.apache.catalina.tribes.MembershipService;
  34. import org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor;
  35. import org.apache.catalina.tribes.io.ChannelData;
  36. import org.apache.catalina.tribes.io.XByteBuffer;
  37. import org.apache.catalina.tribes.UniqueId;
  38. import org.apache.catalina.tribes.Heartbeat;
  39. import org.apache.catalina.tribes.io.BufferPool;
  40. import org.apache.catalina.tribes.RemoteProcessException;
  41. import org.apache.catalina.tribes.util.Logs;
  42. import org.apache.catalina.tribes.util.Arrays;
  43. import org.apache.juli.logging.Log;
  44. import org.apache.juli.logging.LogFactory;
  45. /**
  46. * The default implementation of a Channel.<br>
  47. * The GroupChannel manages the replication channel. It coordinates
  48. * message being sent and received with membership announcements.
  49. * The channel has an chain of interceptors that can modify the message or perform other logic.<br>
  50. * It manages a complete group, both membership and replication.
  51. * @author Filip Hanik
  52. * @version $Id: GroupChannel.java 939305 2010-04-29 13:43:39Z kkolinko $
  53. */
  54. public class GroupChannel extends ChannelInterceptorBase implements ManagedChannel {
  55. private static final Log log = LogFactory.getLog(GroupChannel.class);
  56. /**
  57. * Flag to determine if the channel manages its own heartbeat
  58. * If set to true, the channel will start a local thread for the heart beat.
  59. */
  60. protected boolean heartbeat = true;
  61. /**
  62. * If <code>heartbeat == true</code> then how often do we want this
  63. * heartbeat to run. default is one minute
  64. */
  65. protected long heartbeatSleeptime = 5*1000;//every 5 seconds
  66. /**
  67. * Internal heartbeat thread
  68. */
  69. protected HeartbeatThread hbthread = null;
  70. /**
  71. * The <code>ChannelCoordinator</code> coordinates the bottom layer components:<br>
  72. * - MembershipService<br>
  73. * - ChannelSender <br>
  74. * - ChannelReceiver<br>
  75. */
  76. protected ChannelCoordinator coordinator = new ChannelCoordinator();
  77. /**
  78. * The first interceptor in the interceptor stack.
  79. * The interceptors are chained in a linked list, so we only need a reference to the
  80. * first one
  81. */
  82. protected ChannelInterceptor interceptors = null;
  83. /**
  84. * A list of membership listeners that subscribe to membership announcements
  85. */
  86. protected ArrayList<Object> membershipListeners = new ArrayList<Object>();
  87. /**
  88. * A list of channel listeners that subscribe to incoming messages
  89. */
  90. protected ArrayList<Object> channelListeners = new ArrayList<Object>();
  91. /**
  92. * If set to true, the GroupChannel will check to make sure that
  93. */
  94. protected boolean optionCheck = false;
  95. /**
  96. * Creates a GroupChannel. This constructor will also
  97. * add the first interceptor in the GroupChannel.<br>
  98. * The first interceptor is always the channel itself.
  99. */
  100. public GroupChannel() {
  101. addInterceptor(this);
  102. }
  103. /**
  104. * Adds an interceptor to the stack for message processing<br>
  105. * Interceptors are ordered in the way they are added.<br>
  106. * <code>channel.addInterceptor(A);</code><br>
  107. * <code>channel.addInterceptor(C);</code><br>
  108. * <code>channel.addInterceptor(B);</code><br>
  109. * Will result in a interceptor stack like this:<br>
  110. * <code>A -> C -> B</code><br>
  111. * The complete stack will look like this:<br>
  112. * <code>Channel -> A -> C -> B -> ChannelCoordinator</code><br>
  113. * @param interceptor ChannelInterceptorBase
  114. */
  115. public void addInterceptor(ChannelInterceptor interceptor) {
  116. if ( interceptors == null ) {
  117. interceptors = interceptor;
  118. interceptors.setNext(coordinator);
  119. interceptors.setPrevious(null);
  120. coordinator.setPrevious(interceptors);
  121. } else {
  122. ChannelInterceptor last = interceptors;
  123. while ( last.getNext() != coordinator ) {
  124. last = last.getNext();
  125. }
  126. last.setNext(interceptor);
  127. interceptor.setNext(coordinator);
  128. interceptor.setPrevious(last);
  129. coordinator.setPrevious(interceptor);
  130. }
  131. }
  132. /**
  133. * Sends a heartbeat through the interceptor stack.<br>
  134. * Invoke this method from the application on a periodic basis if
  135. * you have turned off internal heartbeats <code>channel.setHeartbeat(false)</code>
  136. */
  137. @Override
  138. public void heartbeat() {
  139. super.heartbeat();
  140. Iterator<Object> i = membershipListeners.iterator();
  141. while ( i.hasNext() ) {
  142. Object o = i.next();
  143. if ( o instanceof Heartbeat ) ((Heartbeat)o).heartbeat();
  144. }
  145. i = channelListeners.iterator();
  146. while ( i.hasNext() ) {
  147. Object o = i.next();
  148. if ( o instanceof Heartbeat ) ((Heartbeat)o).heartbeat();
  149. }
  150. }
  151. /**
  152. * Send a message to the destinations specified
  153. * @param destination Member[] - destination.length > 1
  154. * @param msg Serializable - the message to send
  155. * @param options int - sender options, options can trigger guarantee levels and different interceptors to
  156. * react to the message see class documentation for the <code>Channel</code> object.<br>
  157. * @return UniqueId - the unique Id that was assigned to this message
  158. * @throws ChannelException - if an error occurs processing the message
  159. * @see org.apache.catalina.tribes.Channel
  160. */
  161. public UniqueId send(Member[] destination, Serializable msg, int options) throws ChannelException {
  162. return send(destination,msg,options,null);
  163. }
  164. /**
  165. *
  166. * @param destination Member[] - destination.length > 1
  167. * @param msg Serializable - the message to send
  168. * @param options int - sender options, options can trigger guarantee levels and different interceptors to
  169. * react to the message see class documentation for the <code>Channel</code> object.<br>
  170. * @param handler - callback object for error handling and completion notification, used when a message is
  171. * sent asynchronously using the <code>Channel.SEND_OPTIONS_ASYNCHRONOUS</code> flag enabled.
  172. * @return UniqueId - the unique Id that was assigned to this message
  173. * @throws ChannelException - if an error occurs processing the message
  174. * @see org.apache.catalina.tribes.Channel
  175. */
  176. public UniqueId send(Member[] destination, Serializable msg, int options, ErrorHandler handler) throws ChannelException {
  177. if ( msg == null ) throw new ChannelException("Cant send a NULL message");
  178. XByteBuffer buffer = null;
  179. try {
  180. if ( destination == null || destination.length == 0) throw new ChannelException("No destination given");
  181. ChannelData data = new ChannelData(true);//generates a unique Id
  182. data.setAddress(getLocalMember(false));
  183. data.setTimestamp(System.currentTimeMillis());
  184. byte[] b = null;
  185. if ( msg instanceof ByteMessage ){
  186. b = ((ByteMessage)msg).getMessage();
  187. options = options | SEND_OPTIONS_BYTE_MESSAGE;
  188. } else {
  189. b = XByteBuffer.serialize(msg);
  190. options = options & (~SEND_OPTIONS_BYTE_MESSAGE);
  191. }
  192. data.setOptions(options);
  193. //XByteBuffer buffer = new XByteBuffer(b.length+128,false);
  194. buffer = BufferPool.getBufferPool().getBuffer(b.length+128, false);
  195. buffer.append(b,0,b.length);
  196. data.setMessage(buffer);
  197. InterceptorPayload payload = null;
  198. if ( handler != null ) {
  199. payload = new InterceptorPayload();
  200. payload.setErrorHandler(handler);
  201. }
  202. getFirstInterceptor().sendMessage(destination, data, payload);
  203. if ( Logs.MESSAGES.isTraceEnabled() ) {
  204. Logs.MESSAGES.trace("GroupChannel - Sent msg:" + new UniqueId(data.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " to "+Arrays.toNameString(destination));
  205. Logs.MESSAGES.trace("GroupChannel - Send Message:" + new UniqueId(data.getUniqueId()) + " is " +msg);
  206. }
  207. return new UniqueId(data.getUniqueId());
  208. }catch ( Exception x ) {
  209. if ( x instanceof ChannelException ) throw (ChannelException)x;
  210. throw new ChannelException(x);
  211. } finally {
  212. if ( buffer != null ) BufferPool.getBufferPool().returnBuffer(buffer);
  213. }
  214. }
  215. /**
  216. * Callback from the interceptor stack. <br>
  217. * When a message is received from a remote node, this method will be invoked by
  218. * the previous interceptor.<br>
  219. * This method can also be used to send a message to other components within the same application,
  220. * but its an extreme case, and you're probably better off doing that logic between the applications itself.
  221. * @param msg ChannelMessage
  222. */
  223. @Override
  224. public void messageReceived(ChannelMessage msg) {
  225. if ( msg == null ) return;
  226. try {
  227. if ( Logs.MESSAGES.isTraceEnabled() ) {
  228. Logs.MESSAGES.trace("GroupChannel - Received msg:" + new UniqueId(msg.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " from "+msg.getAddress().getName());
  229. }
  230. Serializable fwd = null;
  231. if ( (msg.getOptions() & SEND_OPTIONS_BYTE_MESSAGE) == SEND_OPTIONS_BYTE_MESSAGE ) {
  232. fwd = new ByteMessage(msg.getMessage().getBytes());
  233. } else {
  234. try {
  235. fwd = XByteBuffer.deserialize(msg.getMessage().getBytesDirect(), 0, msg.getMessage().getLength());
  236. }catch (Exception sx) {
  237. log.error("Unable to deserialize message:"+msg,sx);
  238. return;
  239. }
  240. }
  241. if ( Logs.MESSAGES.isTraceEnabled() ) {
  242. Logs.MESSAGES.trace("GroupChannel - Receive Message:" + new UniqueId(msg.getUniqueId()) + " is " +fwd);
  243. }
  244. //get the actual member with the correct alive time
  245. Member source = msg.getAddress();
  246. boolean rx = false;
  247. boolean delivered = false;
  248. for ( int i=0; i<channelListeners.size(); i++ ) {
  249. ChannelListener channelListener = (ChannelListener)channelListeners.get(i);
  250. if (channelListener != null && channelListener.accept(fwd, source)) {
  251. channelListener.messageReceived(fwd, source);
  252. delivered = true;
  253. //if the message was accepted by an RPC channel, that channel
  254. //is responsible for returning the reply, otherwise we send an absence reply
  255. if ( channelListener instanceof RpcChannel ) rx = true;
  256. }
  257. }//for
  258. if ((!rx) && (fwd instanceof RpcMessage)) {
  259. //if we have a message that requires a response,
  260. //but none was given, send back an immediate one
  261. sendNoRpcChannelReply((RpcMessage)fwd,source);
  262. }
  263. if ( Logs.MESSAGES.isTraceEnabled() ) {
  264. Logs.MESSAGES.trace("GroupChannel delivered["+delivered+"] id:"+new UniqueId(msg.getUniqueId()));
  265. }
  266. } catch ( Exception x ) {
  267. //this could be the channel listener throwing an exception, we should log it
  268. //as a warning.
  269. if ( log.isWarnEnabled() ) log.warn("Error receiving message:",x);
  270. throw new RemoteProcessException("Exception:"+x.getMessage(),x);
  271. }
  272. }
  273. /**
  274. * Sends a <code>NoRpcChannelReply</code> message to a member<br>
  275. * This method gets invoked by the channel if a RPC message comes in
  276. * and no channel listener accepts the message. This avoids timeout
  277. * @param msg RpcMessage
  278. * @param destination Member - the destination for the reply
  279. */
  280. protected void sendNoRpcChannelReply(RpcMessage msg, Member destination) {
  281. try {
  282. //avoid circular loop
  283. if ( msg instanceof RpcMessage.NoRpcChannelReply) return;
  284. RpcMessage.NoRpcChannelReply reply = new RpcMessage.NoRpcChannelReply(msg.rpcId,msg.uuid);
  285. send(new Member[]{destination},reply,Channel.SEND_OPTIONS_ASYNCHRONOUS);
  286. } catch ( Exception x ) {
  287. log.error("Unable to find rpc channel, failed to send NoRpcChannelReply.",x);
  288. }
  289. }
  290. /**
  291. * memberAdded gets invoked by the interceptor below the channel
  292. * and the channel will broadcast it to the membership listeners
  293. * @param member Member - the new member
  294. */
  295. @Override
  296. public void memberAdded(Member member) {
  297. //notify upwards
  298. for (int i=0; i<membershipListeners.size(); i++ ) {
  299. MembershipListener membershipListener = (MembershipListener)membershipListeners.get(i);
  300. if (membershipListener != null) membershipListener.memberAdded(member);
  301. }
  302. }
  303. /**
  304. * memberDisappeared gets invoked by the interceptor below the channel
  305. * and the channel will broadcast it to the membership listeners
  306. * @param member Member - the member that left or crashed
  307. */
  308. @Override
  309. public void memberDisappeared(Member member) {
  310. //notify upwards
  311. for (int i=0; i<membershipListeners.size(); i++ ) {
  312. MembershipListener membershipListener = (MembershipListener)membershipListeners.get(i);
  313. if (membershipListener != null) membershipListener.memberDisappeared(member);
  314. }
  315. }
  316. /**
  317. * Sets up the default implementation interceptor stack
  318. * if no interceptors have been added
  319. * @throws ChannelException
  320. */
  321. protected synchronized void setupDefaultStack() throws ChannelException {
  322. if ( getFirstInterceptor() != null &&
  323. ((getFirstInterceptor().getNext() instanceof ChannelCoordinator))) {
  324. ChannelInterceptor interceptor = null;
  325. Class<?> clazz = null;
  326. try {
  327. clazz = Class.forName("org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor",
  328. true,GroupChannel.class.getClassLoader());
  329. clazz.newInstance();
  330. } catch ( Throwable x ) {
  331. clazz = MessageDispatchInterceptor.class;
  332. }//catch
  333. try {
  334. interceptor = (ChannelInterceptor) clazz.newInstance();
  335. } catch (Exception x) {
  336. throw new ChannelException("Unable to add MessageDispatchInterceptor to interceptor chain.",x);
  337. }
  338. this.addInterceptor(interceptor);
  339. }
  340. }
  341. /**
  342. * Validates the option flags that each interceptor is using and reports
  343. * an error if two interceptor share the same flag.
  344. * @throws ChannelException
  345. */
  346. protected void checkOptionFlags() throws ChannelException {
  347. StringBuilder conflicts = new StringBuilder();
  348. ChannelInterceptor first = interceptors;
  349. while ( first != null ) {
  350. int flag = first.getOptionFlag();
  351. if ( flag != 0 ) {
  352. ChannelInterceptor next = first.getNext();
  353. while ( next != null ) {
  354. int nflag = next.getOptionFlag();
  355. if (nflag!=0 && (((flag & nflag) == flag ) || ((flag & nflag) == nflag)) ) {
  356. conflicts.append("[");
  357. conflicts.append(first.getClass().getName());
  358. conflicts.append(":");
  359. conflicts.append(flag);
  360. conflicts.append(" == ");
  361. conflicts.append(next.getClass().getName());
  362. conflicts.append(":");
  363. conflicts.append(nflag);
  364. conflicts.append("] ");
  365. }//end if
  366. next = next.getNext();
  367. }//while
  368. }//end if
  369. first = first.getNext();
  370. }//while
  371. if ( conflicts.length() > 0 ) throw new ChannelException("Interceptor option flag conflict: "+conflicts.toString());
  372. }
  373. /**
  374. * Starts the channel
  375. * @param svc int - what service to start
  376. * @throws ChannelException
  377. * @see org.apache.catalina.tribes.Channel#start(int)
  378. */
  379. @Override
  380. public synchronized void start(int svc) throws ChannelException {
  381. setupDefaultStack();
  382. if (optionCheck) checkOptionFlags();
  383. super.start(svc);
  384. if ( hbthread == null && heartbeat ) {
  385. hbthread = new HeartbeatThread(this,heartbeatSleeptime);
  386. hbthread.start();
  387. }
  388. }
  389. /**
  390. * Stops the channel
  391. * @param svc int
  392. * @throws ChannelException
  393. * @see org.apache.catalina.tribes.Channel#stop(int)
  394. */
  395. @Override
  396. public synchronized void stop(int svc) throws ChannelException {
  397. if (hbthread != null) {
  398. hbthread.stopHeartbeat();
  399. hbthread = null;
  400. }
  401. super.stop(svc);
  402. }
  403. /**
  404. * Returns the first interceptor of the stack. Useful for traversal.
  405. * @return ChannelInterceptor
  406. */
  407. public ChannelInterceptor getFirstInterceptor() {
  408. if (interceptors != null) return interceptors;
  409. else return coordinator;
  410. }
  411. /**
  412. * Returns the channel receiver component
  413. * @return ChannelReceiver
  414. */
  415. public ChannelReceiver getChannelReceiver() {
  416. return coordinator.getClusterReceiver();
  417. }
  418. /**
  419. * Returns the channel sender component
  420. * @return ChannelSender
  421. */
  422. public ChannelSender getChannelSender() {
  423. return coordinator.getClusterSender();
  424. }
  425. /**
  426. * Returns the membership service component
  427. * @return MembershipService
  428. */
  429. public MembershipService getMembershipService() {
  430. return coordinator.getMembershipService();
  431. }
  432. /**
  433. * Sets the channel receiver component
  434. * @param clusterReceiver ChannelReceiver
  435. */
  436. public void setChannelReceiver(ChannelReceiver clusterReceiver) {
  437. coordinator.setClusterReceiver(clusterReceiver);
  438. }
  439. /**
  440. * Sets the channel sender component
  441. * @param clusterSender ChannelSender
  442. */
  443. public void setChannelSender(ChannelSender clusterSender) {
  444. coordinator.setClusterSender(clusterSender);
  445. }
  446. /**
  447. * Sets the membership component
  448. * @param membershipService MembershipService
  449. */
  450. public void setMembershipService(MembershipService membershipService) {
  451. coordinator.setMembershipService(membershipService);
  452. }
  453. /**
  454. * Adds a membership listener to the channel.<br>
  455. * Membership listeners are uniquely identified using the equals(Object) method
  456. * @param membershipListener MembershipListener
  457. */
  458. public void addMembershipListener(MembershipListener membershipListener) {
  459. if (!this.membershipListeners.contains(membershipListener) )
  460. this.membershipListeners.add(membershipListener);
  461. }
  462. /**
  463. * Removes a membership listener from the channel.<br>
  464. * Membership listeners are uniquely identified using the equals(Object) method
  465. * @param membershipListener MembershipListener
  466. */
  467. public void removeMembershipListener(MembershipListener membershipListener) {
  468. membershipListeners.remove(membershipListener);
  469. }
  470. /**
  471. * Adds a channel listener to the channel.<br>
  472. * Channel listeners are uniquely identified using the equals(Object) method
  473. * @param channelListener ChannelListener
  474. */
  475. public void addChannelListener(ChannelListener channelListener) {
  476. if (!this.channelListeners.contains(channelListener) ) {
  477. this.channelListeners.add(channelListener);
  478. } else {
  479. throw new IllegalArgumentException("Listener already exists:"+channelListener+"["+channelListener.getClass().getName()+"]");
  480. }
  481. }
  482. /**
  483. *
  484. * Removes a channel listener from the channel.<br>
  485. * Channel listeners are uniquely identified using the equals(Object) method
  486. * @param channelListener ChannelListener
  487. */
  488. public void removeChannelListener(ChannelListener channelListener) {
  489. channelListeners.remove(channelListener);
  490. }
  491. /**
  492. * Returns an iterator of all the interceptors in this stack
  493. * @return Iterator
  494. */
  495. public Iterator<ChannelInterceptor> getInterceptors() {
  496. return new InterceptorIterator(this.getNext(),this.coordinator);
  497. }
  498. /**
  499. * Enables/disables the option check<br>
  500. * Setting this to true, will make the GroupChannel perform a conflict check
  501. * on the interceptors. If two interceptors are using the same option flag
  502. * and throw an error upon start.
  503. * @param optionCheck boolean
  504. */
  505. public void setOptionCheck(boolean optionCheck) {
  506. this.optionCheck = optionCheck;
  507. }
  508. /**
  509. * Configure local heartbeat sleep time<br>
  510. * Only used when <code>getHeartbeat()==true</code>
  511. * @param heartbeatSleeptime long - time in milliseconds to sleep between heartbeats
  512. */
  513. public void setHeartbeatSleeptime(long heartbeatSleeptime) {
  514. this.heartbeatSleeptime = heartbeatSleeptime;
  515. }
  516. /**
  517. * Enables or disables local heartbeat.
  518. * if <code>setHeartbeat(true)</code> is invoked then the channel will start an internal
  519. * thread to invoke <code>Channel.heartbeat()</code> every <code>getHeartbeatSleeptime</code> milliseconds
  520. * @param heartbeat boolean
  521. */
  522. public void setHeartbeat(boolean heartbeat) {
  523. this.heartbeat = heartbeat;
  524. }
  525. /**
  526. * @see #setOptionCheck(boolean)
  527. * @return boolean
  528. */
  529. public boolean getOptionCheck() {
  530. return optionCheck;
  531. }
  532. /**
  533. * @see #setHeartbeat(boolean)
  534. * @return boolean
  535. */
  536. public boolean getHeartbeat() {
  537. return heartbeat;
  538. }
  539. /**
  540. * Returns the sleep time in milliseconds that the internal heartbeat will
  541. * sleep in between invocations of <code>Channel.heartbeat()</code>
  542. * @return long
  543. */
  544. public long getHeartbeatSleeptime() {
  545. return heartbeatSleeptime;
  546. }
  547. /**
  548. *
  549. * <p>Title: Interceptor Iterator</p>
  550. *
  551. * <p>Description: An iterator to loop through the interceptors in a channel</p>
  552. *
  553. * @version 1.0
  554. */
  555. public static class InterceptorIterator implements Iterator<ChannelInterceptor> {
  556. private ChannelInterceptor end;
  557. private ChannelInterceptor start;
  558. public InterceptorIterator(ChannelInterceptor start, ChannelInterceptor end) {
  559. this.end = end;
  560. this.start = start;
  561. }
  562. public boolean hasNext() {
  563. return start!=null && start != end;
  564. }
  565. public ChannelInterceptor next() {
  566. ChannelInterceptor result = null;
  567. if ( hasNext() ) {
  568. result = start;
  569. start = start.getNext();
  570. }
  571. return result;
  572. }
  573. public void remove() {
  574. //empty operation
  575. }
  576. }
  577. /**
  578. *
  579. * <p>Title: Internal heartbeat thread</p>
  580. *
  581. * <p>Description: if <code>Channel.getHeartbeat()==true</code> then a thread of this class
  582. * is created</p>
  583. *
  584. * @version 1.0
  585. */
  586. public static class HeartbeatThread extends Thread {
  587. private static final Log log = LogFactory.getLog(HeartbeatThread.class);
  588. protected static int counter = 1;
  589. protected static synchronized int inc() {
  590. return counter++;
  591. }
  592. protected volatile boolean doRun = true;
  593. protected GroupChannel channel;
  594. protected long sleepTime;
  595. public HeartbeatThread(GroupChannel channel, long sleepTime) {
  596. super();
  597. this.setPriority(MIN_PRIORITY);
  598. setName("GroupChannel-Heartbeat-"+inc());
  599. setDaemon(true);
  600. this.channel = channel;
  601. this.sleepTime = sleepTime;
  602. }
  603. public void stopHeartbeat() {
  604. doRun = false;
  605. interrupt();
  606. }
  607. @Override
  608. public void run() {
  609. while (doRun) {
  610. try {
  611. Thread.sleep(sleepTime);
  612. channel.heartbeat();
  613. } catch ( InterruptedException x ) {
  614. interrupted();
  615. } catch ( Exception x ) {
  616. log.error("Unable to send heartbeat through Tribes interceptor stack. Will try to sleep again.",x);
  617. }//catch
  618. }//while
  619. }//run
  620. }//HeartbeatThread
  621. }