/** * Copyright (C) 2012 FuseSource, Inc. * http://fusesource.com * * 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.fusesource.hawtdispatch.transport; import org.fusesource.hawtdispatch.Dispatch; import org.fusesource.hawtdispatch.Task; import java.util.concurrent.TimeUnit; /** * <p>A HeartBeatMonitor can be used to watch the read and write * activity of a transport and raise events when the write side * or read side has been idle too long.</p> * * @author <a href="http://hiramchirino.com">Hiram Chirino</a> */ public class HeartBeatMonitor { Transport transport; long initialWriteCheckDelay; long initialReadCheckDelay; long writeInterval; long readInterval; Task onKeepAlive = Dispatch.NOOP; Task onDead = Dispatch.NOOP; volatile short session = 0; boolean readSuspendedInterval; short readSuspendCount; Object lock = new Object(); public void suspendRead() { readSuspendCount++; readSuspendedInterval = true; } public void resumeRead() { readSuspendCount--; } private void schedule(final short session, long interval, final Task func) { if (this.session == session) { transport.getDispatchQueue().executeAfter(interval, TimeUnit.MILLISECONDS, new Task() { public void run() { synchronized (lock) { if (HeartBeatMonitor.this.session == session) { func.run(); } } } }); } } private void scheduleCheckWrites(final short session) { final ProtocolCodec codec = transport.getProtocolCodec(); Task func; if (codec == null) { func = new Task() { public void run() { scheduleCheckWrites(session); } }; } else { final long lastWriteCounter = codec.getWriteCounter(); func = new Task() { public void run() { if (lastWriteCounter == codec.getWriteCounter()) { onKeepAlive.run(); } scheduleCheckWrites(session); } }; } schedule(session, writeInterval, func); } private void scheduleCheckReads(final short session) { final ProtocolCodec codec = transport.getProtocolCodec(); Task func; if (codec == null) { func = new Task() { public void run() { scheduleCheckReads(session); } }; } else { final long lastReadCounter = codec.getReadCounter(); func = new Task() { public void run() { if (lastReadCounter == codec.getReadCounter() && !readSuspendedInterval && readSuspendCount == 0) { onDead.run(); } readSuspendedInterval = false; scheduleCheckReads(session); } }; } schedule(session, readInterval, func); } public void start() { session++; readSuspendedInterval = false; if (writeInterval != 0) { if (initialWriteCheckDelay != 0) { transport.getDispatchQueue().executeAfter(initialWriteCheckDelay, TimeUnit.MILLISECONDS, new Task() { public void run() { scheduleCheckWrites(session); } }); } else { scheduleCheckWrites(session); } } if (readInterval != 0) { if (initialReadCheckDelay != 0) { transport.getDispatchQueue().executeAfter(initialReadCheckDelay, TimeUnit.MILLISECONDS, new Task() { public void run() { scheduleCheckReads(session); } }); } else { scheduleCheckReads(session); } } } public void stop() { synchronized (lock) { session++; } } public long getInitialReadCheckDelay() { return initialReadCheckDelay; } public void setInitialReadCheckDelay(long initialReadCheckDelay) { this.initialReadCheckDelay = initialReadCheckDelay; } public long getInitialWriteCheckDelay() { return initialWriteCheckDelay; } public void setInitialWriteCheckDelay(long initialWriteCheckDelay) { this.initialWriteCheckDelay = initialWriteCheckDelay; } public Task getOnDead() { return onDead; } public void setOnDead(Task onDead) { this.onDead = onDead; } public Task getOnKeepAlive() { return onKeepAlive; } public void setOnKeepAlive(Task onKeepAlive) { this.onKeepAlive = onKeepAlive; } public long getWriteInterval() { return writeInterval; } public void setWriteInterval(long writeInterval) { this.writeInterval = writeInterval; } public Transport getTransport() { return transport; } public void setTransport(Transport transport) { this.transport = transport; } public long getReadInterval() { return readInterval; } public void setReadInterval(long readInterval) { this.readInterval = readInterval; } }