001 /**
002 * Copyright (C) 2012 FuseSource, Inc.
003 * http://fusesource.com
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.fusesource.hawtdispatch.transport;
019
020 import org.fusesource.hawtdispatch.Dispatch;
021
022 import java.util.concurrent.TimeUnit;
023
024 /**
025 * <p>A HeartBeatMonitor can be used to watch the read and write
026 * activity of a transport and raise events when the write side
027 * or read side has been idle too long.</p>
028 *
029 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
030 */
031 public class HeartBeatMonitor {
032
033 Transport transport;
034 long initialWriteCheckDelay;
035 long initialReadCheckDelay;
036 long writeInterval;
037 long readInterval;
038
039 Runnable onKeepAlive = Dispatch.NOOP;
040 Runnable onDead = Dispatch.NOOP;
041
042 short session = 0;
043
044 boolean readSuspendedInterval;
045 short readSuspendCount;
046
047 public void suspendRead() {
048 readSuspendCount++;
049 readSuspendedInterval = true;
050 }
051
052 public void resumeRead() {
053 readSuspendCount--;
054 }
055
056 private void schedule(final short session, long interval, final Runnable func) {
057 if (this.session == session) {
058 transport.getDispatchQueue().executeAfter(interval, TimeUnit.MILLISECONDS, new Runnable() {
059 public void run() {
060 if (HeartBeatMonitor.this.session == session) {
061 func.run();
062 }
063 }
064 });
065 }
066 }
067
068 private void scheduleCheckWrites(final short session) {
069 final ProtocolCodec codec = transport.getProtocolCodec();
070 Runnable func;
071 if (codec == null) {
072 func = new Runnable() {
073 public void run() {
074 scheduleCheckWrites(session);
075 }
076 };
077 } else {
078 final long lastWriteCounter = codec.getWriteCounter();
079 func = new Runnable() {
080 public void run() {
081 if (lastWriteCounter == codec.getWriteCounter()) {
082 onKeepAlive.run();
083 }
084 scheduleCheckWrites(session);
085 }
086 };
087 }
088 schedule(session, writeInterval, func);
089 }
090
091 private void scheduleCheckReads(final short session) {
092 final ProtocolCodec codec = transport.getProtocolCodec();
093 Runnable func;
094 if (codec == null) {
095 func = new Runnable() {
096 public void run() {
097 scheduleCheckReads(session);
098 }
099 };
100 } else {
101 final long lastReadCounter = codec.getReadCounter();
102 func = new Runnable() {
103 public void run() {
104 if (lastReadCounter == codec.getReadCounter() && !readSuspendedInterval && readSuspendCount == 0) {
105 onDead.run();
106 }
107 readSuspendedInterval = false;
108 scheduleCheckReads(session);
109 }
110 };
111 }
112 schedule(session, readInterval, func);
113 }
114
115 public void start() {
116 session++;
117 readSuspendedInterval = false;
118 if (writeInterval != 0) {
119 if (initialWriteCheckDelay != 0) {
120 transport.getDispatchQueue().executeAfter(initialWriteCheckDelay, TimeUnit.MILLISECONDS, new Runnable() {
121 public void run() {
122 scheduleCheckWrites(session);
123 }
124 });
125 } else {
126 scheduleCheckWrites(session);
127 }
128 }
129 if (readInterval != 0) {
130 if (initialReadCheckDelay != 0) {
131 transport.getDispatchQueue().executeAfter(initialReadCheckDelay, TimeUnit.MILLISECONDS, new Runnable() {
132 public void run() {
133 scheduleCheckReads(session);
134 }
135 });
136 } else {
137 scheduleCheckReads(session);
138 }
139 }
140 }
141
142 public void stop() {
143 session++;
144 }
145
146
147 public long getInitialReadCheckDelay() {
148 return initialReadCheckDelay;
149 }
150
151 public void setInitialReadCheckDelay(long initialReadCheckDelay) {
152 this.initialReadCheckDelay = initialReadCheckDelay;
153 }
154
155 public long getInitialWriteCheckDelay() {
156 return initialWriteCheckDelay;
157 }
158
159 public void setInitialWriteCheckDelay(long initialWriteCheckDelay) {
160 this.initialWriteCheckDelay = initialWriteCheckDelay;
161 }
162
163 public Runnable getOnDead() {
164 return onDead;
165 }
166
167 public void setOnDead(Runnable onDead) {
168 this.onDead = onDead;
169 }
170
171 public Runnable getOnKeepAlive() {
172 return onKeepAlive;
173 }
174
175 public void setOnKeepAlive(Runnable onKeepAlive) {
176 this.onKeepAlive = onKeepAlive;
177 }
178
179 public long getWriteInterval() {
180 return writeInterval;
181 }
182
183 public void setWriteInterval(long writeInterval) {
184 this.writeInterval = writeInterval;
185 }
186
187 public Transport getTransport() {
188 return transport;
189 }
190
191 public void setTransport(Transport transport) {
192 this.transport = transport;
193 }
194
195 public long getReadInterval() {
196 return readInterval;
197 }
198
199 public void setReadInterval(long readInterval) {
200 this.readInterval = readInterval;
201 }
202 }