io.nats.client.impl.MessageManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jnats Show documentation
Show all versions of jnats Show documentation
Client library for working with the NATS messaging system.
The newest version!
// Copyright 2021 The NATS Authors
// 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 io.nats.client.impl;
import io.nats.client.Message;
import io.nats.client.PullRequestOptions;
import io.nats.client.SubscribeOptions;
import java.time.Duration;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
abstract class MessageManager {
public enum ManageResult {MESSAGE, STATUS_HANDLED, STATUS_TERMINUS, STATUS_ERROR}
protected static final int THRESHOLD = 3;
protected final ReentrantLock stateChangeLock;
protected final NatsConnection conn;
protected final SubscribeOptions so;
protected final boolean syncMode;
protected NatsJetStreamSubscription sub; // not final it is not set until after construction
protected long lastStreamSeq;
protected long lastConsumerSeq;
protected AtomicLong lastMsgReceived;
// heartbeat stuff
protected boolean hb;
protected long idleHeartbeatSetting;
protected long alarmPeriodSetting;
protected MmTimerTask heartbeatTimerTask;
protected Timer heartbeatTimer;
protected MessageManager(NatsConnection conn, SubscribeOptions so, boolean syncMode) {
stateChangeLock = new ReentrantLock();
this.conn = conn;
this.so = so;
this.syncMode = syncMode;
lastStreamSeq = 0;
lastConsumerSeq = 0;
hb = false;
idleHeartbeatSetting = 0;
alarmPeriodSetting = 0;
lastMsgReceived = new AtomicLong(System.currentTimeMillis());
}
protected boolean isSyncMode() { return syncMode; }
protected long getLastStreamSequence() { return lastStreamSeq; }
protected long getLastConsumerSequence() { return lastConsumerSeq; }
protected long getLastMsgReceived() { return lastMsgReceived.get(); }
protected boolean isHb() { return hb; }
protected long getIdleHeartbeatSetting() { return idleHeartbeatSetting; }
protected long getAlarmPeriodSetting() { return alarmPeriodSetting; }
protected void startup(NatsJetStreamSubscription sub) {
this.sub = sub;
}
protected void shutdown() {
shutdownHeartbeatTimer();
}
protected void startPullRequest(String pullSubject, PullRequestOptions pullRequestOptions, boolean raiseStatusWarnings, PullManagerObserver pullManagerObserver) {
// does nothing - only implemented for pulls, but in base class since instance is referenced as MessageManager, not subclass
}
protected Boolean beforeQueueProcessorImpl(NatsMessage msg) {
return true;
}
abstract protected ManageResult manage(Message msg);
protected void trackJsMessage(Message msg) {
stateChangeLock.lock();
try {
NatsJetStreamMetaData meta = msg.metaData();
lastStreamSeq = meta.streamSequence();
lastConsumerSeq++;
}
finally {
stateChangeLock.unlock();
}
}
protected void handleHeartbeatError() {
conn.executeCallback((c, el) -> el.heartbeatAlarm(c, sub, lastStreamSeq, lastConsumerSeq));
}
protected void configureIdleHeartbeat(Duration configIdleHeartbeat, long configMessageAlarmTime) {
stateChangeLock.lock();
try {
idleHeartbeatSetting = configIdleHeartbeat == null ? 0 : configIdleHeartbeat.toMillis();
if (idleHeartbeatSetting <= 0) {
alarmPeriodSetting = 0;
hb = false;
}
else {
if (configMessageAlarmTime < idleHeartbeatSetting) {
alarmPeriodSetting = idleHeartbeatSetting * THRESHOLD;
}
else {
alarmPeriodSetting = configMessageAlarmTime;
}
hb = true;
}
}
finally {
stateChangeLock.unlock();
}
}
protected void updateLastMessageReceived() {
lastMsgReceived.set(System.currentTimeMillis());
}
class MmTimerTask extends TimerTask {
long alarmPeriod;
final AtomicBoolean alive;
public MmTimerTask(long alarmPeriod) {
this.alarmPeriod = alarmPeriod;
alive = new AtomicBoolean(true);
}
public void reuse() {
alive.getAndSet(true);
}
public void shutdown() {
alive.getAndSet(false);
}
@Override
public void run() {
if (alive.get() && !Thread.interrupted()) {
long sinceLast = System.currentTimeMillis() - lastMsgReceived.get();
if (alive.get() && sinceLast > alarmPeriodSetting) {
handleHeartbeatError();
}
}
}
@Override
public String toString() {
long sinceLast = System.currentTimeMillis() - lastMsgReceived.get();
return "MmTimerTask{" +
", alarmPeriod=" + alarmPeriod +
", alive=" + alive.get() +
", sinceLast=" + sinceLast +
'}';
}
}
protected void initOrResetHeartbeatTimer() {
stateChangeLock.lock();
try {
if (heartbeatTimer != null) {
// Same settings, just reuse the existing timer
if (heartbeatTimerTask.alarmPeriod == alarmPeriodSetting) {
heartbeatTimerTask.reuse();
updateLastMessageReceived();
return;
}
// Replace timer since settings have changed
shutdownHeartbeatTimer();
}
// replacement or new comes here
heartbeatTimer = new Timer();
heartbeatTimerTask = new MmTimerTask(alarmPeriodSetting);
heartbeatTimer.schedule(heartbeatTimerTask, alarmPeriodSetting, alarmPeriodSetting);
updateLastMessageReceived();
}
finally {
stateChangeLock.unlock();
}
}
protected void shutdownHeartbeatTimer() {
stateChangeLock.lock();
try {
if (heartbeatTimer != null) {
heartbeatTimerTask.shutdown();
heartbeatTimerTask = null;
heartbeatTimer.cancel();
heartbeatTimer = null;
}
}
finally {
stateChangeLock.unlock();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy