001 /**
002 * Copyright (C) 2010-2011, FuseSource Corp. All rights reserved.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.fusesource.hawtdispatch.transport;
017
018 import org.fusesource.hawtdispatch.Dispatch;
019
020 import java.util.concurrent.TimeUnit;
021
022 /**
023 * <p>A HeartBeatMonitor can be used to watch the read and write
024 * activity of a transport and raise events when the write side
025 * or read side has been idle too long.</p>
026 *
027 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
028 */
029 public class HeartBeatMonitor {
030
031 Transport transport;
032 long initialWriteCheckDelay;
033 long initialReadCheckDelay;
034 long writeInterval;
035 long readInterval;
036
037 Runnable onKeepAlive = Dispatch.NOOP;
038 Runnable onDead = Dispatch.NOOP;
039
040 short session = 0;
041
042 boolean readSuspendedInterval;
043 short readSuspendCount;
044
045 public void suspendRead() {
046 readSuspendCount++;
047 readSuspendedInterval = true;
048 }
049
050 public void resumeRead() {
051 readSuspendCount--;
052 }
053
054 private void schedule(final short session, long interval, final Runnable func) {
055 if (this.session == session) {
056 transport.getDispatchQueue().executeAfter(interval, TimeUnit.MILLISECONDS, new Runnable() {
057 public void run() {
058 if (HeartBeatMonitor.this.session == session) {
059 func.run();
060 }
061 }
062 });
063 }
064 }
065
066 private void scheduleCheckWrites(final short session) {
067 final ProtocolCodec codec = transport.getProtocolCodec();
068 Runnable func;
069 if (codec == null) {
070 func = new Runnable() {
071 public void run() {
072 scheduleCheckWrites(session);
073 }
074 };
075 } else {
076 final long lastWriteCounter = codec.getWriteCounter();
077 func = new Runnable() {
078 public void run() {
079 if (lastWriteCounter == codec.getWriteCounter()) {
080 onKeepAlive.run();
081 }
082 scheduleCheckWrites(session);
083 }
084 };
085 }
086 schedule(session, writeInterval, func);
087 }
088
089 private void scheduleCheckReads(final short session) {
090 final ProtocolCodec codec = transport.getProtocolCodec();
091 Runnable func;
092 if (codec == null) {
093 func = new Runnable() {
094 public void run() {
095 scheduleCheckReads(session);
096 }
097 };
098 } else {
099 final long lastReadCounter = codec.getReadCounter();
100 func = new Runnable() {
101 public void run() {
102 if (lastReadCounter == codec.getReadCounter() && !readSuspendedInterval && readSuspendCount == 0) {
103 onDead.run();
104 }
105 readSuspendedInterval = false;
106 scheduleCheckReads(session);
107 }
108 };
109 }
110 schedule(session, readInterval, func);
111 }
112
113 public void start() {
114 session++;
115 readSuspendedInterval = false;
116 if (writeInterval != 0) {
117 if (initialWriteCheckDelay != 0) {
118 transport.getDispatchQueue().executeAfter(initialWriteCheckDelay, TimeUnit.MILLISECONDS, new Runnable() {
119 public void run() {
120 scheduleCheckWrites(session);
121 }
122 });
123 } else {
124 scheduleCheckWrites(session);
125 }
126 }
127 if (readInterval != 0) {
128 if (initialReadCheckDelay != 0) {
129 transport.getDispatchQueue().executeAfter(initialReadCheckDelay, TimeUnit.MILLISECONDS, new Runnable() {
130 public void run() {
131 scheduleCheckReads(session);
132 }
133 });
134 } else {
135 scheduleCheckReads(session);
136 }
137 }
138 }
139
140 public void stop() {
141 session++;
142 }
143
144
145 public long getInitialReadCheckDelay() {
146 return initialReadCheckDelay;
147 }
148
149 public void setInitialReadCheckDelay(long initialReadCheckDelay) {
150 this.initialReadCheckDelay = initialReadCheckDelay;
151 }
152
153 public long getInitialWriteCheckDelay() {
154 return initialWriteCheckDelay;
155 }
156
157 public void setInitialWriteCheckDelay(long initialWriteCheckDelay) {
158 this.initialWriteCheckDelay = initialWriteCheckDelay;
159 }
160
161 public Runnable getOnDead() {
162 return onDead;
163 }
164
165 public void setOnDead(Runnable onDead) {
166 this.onDead = onDead;
167 }
168
169 public Runnable getOnKeepAlive() {
170 return onKeepAlive;
171 }
172
173 public void setOnKeepAlive(Runnable onKeepAlive) {
174 this.onKeepAlive = onKeepAlive;
175 }
176
177 public long getWriteInterval() {
178 return writeInterval;
179 }
180
181 public void setWriteInterval(long writeInterval) {
182 this.writeInterval = writeInterval;
183 }
184
185 public Transport getTransport() {
186 return transport;
187 }
188
189 public void setTransport(Transport transport) {
190 this.transport = transport;
191 }
192
193 public long getReadInterval() {
194 return readInterval;
195 }
196
197 public void setReadInterval(long readInterval) {
198 this.readInterval = readInterval;
199 }
200 }