io.nats.client.impl.NatsJetStream 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.
// 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.*;
import io.nats.client.api.*;
import io.nats.client.support.Validator;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static io.nats.client.PushSubscribeOptions.DEFAULT_PUSH_OPTS;
import static io.nats.client.impl.MessageManager.ManageResult;
import static io.nats.client.support.NatsJetStreamClientError.*;
import static io.nats.client.support.NatsRequestCompletableFuture.CancelAction;
import static io.nats.client.support.Validator.*;
public class NatsJetStream extends NatsJetStreamImpl implements JetStream {
public NatsJetStream(NatsConnection connection, JetStreamOptions jsOptions) throws IOException {
super(connection, jsOptions);
}
NatsJetStream(NatsJetStreamImpl impl) {
super(impl);
}
// ----------------------------------------------------------------------------------------------------
// Publish
// ----------------------------------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public PublishAck publish(String subject, byte[] body) throws IOException, JetStreamApiException {
return publishSyncInternal(subject, null, body, null, true);
}
/**
* {@inheritDoc}
*/
@Override
public PublishAck publish(String subject, Headers headers, byte[] body) throws IOException, JetStreamApiException {
return publishSyncInternal(subject, headers, body, null, true);
}
/**
* {@inheritDoc}
*/
@Override
public PublishAck publish(String subject, byte[] body, PublishOptions options) throws IOException, JetStreamApiException {
return publishSyncInternal(subject, null, body, options, true);
}
/**
* {@inheritDoc}
*/
@Override
public PublishAck publish(String subject, Headers headers, byte[] body, PublishOptions options) throws IOException, JetStreamApiException {
return publishSyncInternal(subject, headers, body, options, true);
}
/**
* {@inheritDoc}
*/
@Override
public PublishAck publish(Message message) throws IOException, JetStreamApiException {
validateNotNull(message, "Message");
return publishSyncInternal(message.getSubject(), message.getHeaders(), message.getData(), null, false);
}
/**
* {@inheritDoc}
*/
@Override
public PublishAck publish(Message message, PublishOptions options) throws IOException, JetStreamApiException {
validateNotNull(message, "Message");
return publishSyncInternal(message.getSubject(), message.getHeaders(), message.getData(), options, false);
}
/**
* {@inheritDoc}
*/
@Override
public CompletableFuture publishAsync(String subject, byte[] body) {
return publishAsyncInternal(subject, null, body, null, null, true);
}
/**
* {@inheritDoc}
*/
@Override
public CompletableFuture publishAsync(String subject, Headers headers, byte[] body) {
return publishAsyncInternal(subject, headers, body, null, null, true);
}
/**
* {@inheritDoc}
*/
@Override
public CompletableFuture publishAsync(String subject, byte[] body, PublishOptions options) {
return publishAsyncInternal(subject, null, body, options, null, true);
}
/**
* {@inheritDoc}
*/
@Override
public CompletableFuture publishAsync(String subject, Headers headers, byte[] body, PublishOptions options) {
return publishAsyncInternal(subject, headers, body, options, null, true);
}
/**
* {@inheritDoc}
*/
@Override
public CompletableFuture publishAsync(Message message) {
validateNotNull(message, "Message");
return publishAsyncInternal(message.getSubject(), message.getHeaders(), message.getData(), null, null, false);
}
/**
* {@inheritDoc}
*/
@Override
public CompletableFuture publishAsync(Message message, PublishOptions options) {
validateNotNull(message, "Message");
return publishAsyncInternal(message.getSubject(), message.getHeaders(), message.getData(), options, null, false);
}
private PublishAck publishSyncInternal(String subject, Headers headers, byte[] data, PublishOptions options, boolean validateSubRep) throws IOException, JetStreamApiException {
Headers merged = mergePublishOptions(headers, options);
if (jso.isPublishNoAck()) {
conn.publishInternal(subject, null, merged, data, validateSubRep);
return null;
}
Duration timeout = options == null ? jso.getRequestTimeout() : options.getStreamTimeout();
Message resp = makeInternalRequestResponseRequired(subject, merged, data, timeout, CancelAction.COMPLETE, validateSubRep);
return processPublishResponse(resp, options);
}
private CompletableFuture publishAsyncInternal(String subject, Headers headers, byte[] data, PublishOptions options, Duration knownTimeout, boolean validateSubRep) {
Headers merged = mergePublishOptions(headers, options);
if (jso.isPublishNoAck()) {
conn.publishInternal(subject, null, merged, data, validateSubRep);
return null;
}
CompletableFuture future = conn.requestFutureInternal(subject, merged, data, knownTimeout, CancelAction.COMPLETE, validateSubRep);
return future.thenCompose(resp -> {
try {
responseRequired(resp);
return CompletableFuture.completedFuture(processPublishResponse(resp, options));
} catch (IOException | JetStreamApiException e) {
throw new RuntimeException(e);
}
});
}
private PublishAck processPublishResponse(Message resp, PublishOptions options) throws IOException, JetStreamApiException {
if (resp.isStatusMessage()) {
throw new IOException("Error Publishing: " + resp.getStatus().getMessageWithCode());
}
PublishAck ack = new PublishAck(resp);
String ackStream = ack.getStream();
String pubStream = options == null ? null : options.getStream();
// stream specified in options but different from ack should not happen but...
if (pubStream != null && !pubStream.equals(ackStream)) {
throw new IOException("Expected ack from stream " + pubStream + ", received from: " + ackStream);
}
return ack;
}
private Headers mergePublishOptions(Headers headers, PublishOptions opts) {
// never touch the user's original headers
Headers merged = headers == null ? null : new Headers(headers);
if (opts != null) {
merged = mergeNum(merged, EXPECTED_LAST_SEQ_HDR, opts.getExpectedLastSequence());
merged = mergeNum(merged, EXPECTED_LAST_SUB_SEQ_HDR, opts.getExpectedLastSubjectSequence());
merged = mergeString(merged, EXPECTED_LAST_MSG_ID_HDR, opts.getExpectedLastMsgId());
merged = mergeString(merged, EXPECTED_STREAM_HDR, opts.getExpectedStream());
merged = mergeString(merged, MSG_ID_HDR, opts.getMessageId());
}
return merged;
}
private Headers mergeNum(Headers h, String key, long value) {
return value > -1 ? _mergeNum(h, key, Long.toString(value)): h;
}
private Headers mergeString(Headers h, String key, String value) {
return Validator.nullOrEmpty(value) ? h : _mergeNum(h, key, value);
}
private Headers _mergeNum(Headers h, String key, String value) {
if (h == null) {
h = new Headers();
}
return h.add(key, value);
}
// ----------------------------------------------------------------------------------------------------
// Subscribe
// ----------------------------------------------------------------------------------------------------
interface MessageManagerFactory {
MessageManager createMessageManager(
NatsConnection conn, NatsJetStream js, String stream,
SubscribeOptions so, ConsumerConfiguration cc, boolean queueMode, boolean syncMode);
}
MessageManagerFactory _pushMessageManagerFactory = PushMessageManager::new;
MessageManagerFactory _pushOrderedMessageManagerFactory = OrderedMessageManager::new;
MessageManagerFactory _pullMessageManagerFactory =
(mmConn, mmJs, mmStream, mmSo, mmCc, mmQueueMode, mmSyncMode) -> new PullMessageManager(mmConn, mmSo, mmSyncMode);
MessageManagerFactory _pullOrderedMessageManagerFactory =
(mmConn, mmJs, mmStream, mmSo, mmCc, mmQueueMode, mmSyncMode) -> new PullOrderedMessageManager(mmConn, mmJs, mmStream, mmSo, mmCc, mmSyncMode);
JetStreamSubscription createSubscription(String userSubscribeSubject,
PushSubscribeOptions pushSubscribeOptions,
PullSubscribeOptions pullSubscribeOptions,
String queueName,
NatsDispatcher dispatcher,
MessageHandler userHandler,
boolean isAutoAck,
PullMessageManager pmmInstance) throws IOException, JetStreamApiException {
// Parameter notes. For those relating to the callers, you can see all the callers further down in this source file.
// - pull subscribe callers guarantee that pullSubscribeOptions is not null
// - qgroup is always null with pull callers
// - callers only ever provide one of the subscribe options
// 1. Initial prep and validation
boolean isPullMode = pullSubscribeOptions != null;
SubscribeOptions so;
String stream;
ConsumerConfiguration userCC;
String settledDeliverGroup = null; // push might set this
if (isPullMode) {
so = pullSubscribeOptions; // options must have already been checked to be non-null
stream = pullSubscribeOptions.getStream();
userCC = so.getConsumerConfiguration();
validateNotSupplied(userCC.getDeliverGroup(), JsSubPullCantHaveDeliverGroup);
validateNotSupplied(userCC.getDeliverSubject(), JsSubPullCantHaveDeliverSubject);
}
else {
so = pushSubscribeOptions == null ? DEFAULT_PUSH_OPTS : pushSubscribeOptions;
stream = so.getStream();
userCC = so.getConsumerConfiguration();
if (userCC.maxPullWaitingWasSet()) { throw JsSubPushCantHaveMaxPullWaiting.instance(); }
if (userCC.maxBatchWasSet()) { throw JsSubPushCantHaveMaxBatch.instance(); }
if (userCC.maxBytesWasSet()) { throw JsSubPushCantHaveMaxBytes.instance(); }
// figure out the queue name
settledDeliverGroup = validateMustMatchIfBothSupplied(userCC.getDeliverGroup(), queueName, JsSubQueueDeliverGroupMismatch);
if (so.isOrdered() && settledDeliverGroup != null) {
throw JsSubOrderedNotAllowOnQueues.instance();
}
if (dispatcher != null &&
(so.getPendingMessageLimit() != Consumer.DEFAULT_MAX_MESSAGES ||
so.getPendingByteLimit() != Consumer.DEFAULT_MAX_BYTES))
{
throw JsSubPushAsyncCantSetPending.instance();
}
}
// 1B. Flow Control / heartbeat not always valid
if (userCC.getIdleHeartbeat() != null && userCC.getIdleHeartbeat().toMillis() > 0) {
if (isPullMode) {
throw JsSubFcHbNotValidPull.instance();
}
if (settledDeliverGroup != null) {
throw JsSubFcHbNotValidQueue.instance();
}
}
// 2. figure out user provided subjects and prepare the settledFilterSubjects
userSubscribeSubject = emptyAsNull(userSubscribeSubject);
List settledFilterSubjects = new ArrayList<>();
if (userCC.getFilterSubjects() == null) { // empty filterSubjects gives null
// userCC.filterSubjects empty, populate settledFilterSubjects w/userSubscribeSubject if possible
if (userSubscribeSubject != null) {
settledFilterSubjects.add(userSubscribeSubject);
}
}
else {
// userCC.filterSubjects not empty, validate them
settledFilterSubjects.addAll(userCC.getFilterSubjects());
// If userSubscribeSubject is provided it must be one of the filter subjects.
if (userSubscribeSubject != null && !settledFilterSubjects.contains(userSubscribeSubject)) {
throw JsSubSubjectDoesNotMatchFilter.instance();
}
}
// 3. Did they tell me what stream? No? look it up.
final String settledStream;
if (stream == null) {
if (settledFilterSubjects.isEmpty()) {
throw JsSubSubjectNeededToLookupStream.instance();
}
settledStream = lookupStreamBySubject(settledFilterSubjects.get(0));
if (settledStream == null) {
throw JsSubNoMatchingStreamForSubject.instance();
}
}
else {
settledStream = stream;
}
ConsumerConfiguration serverCC = null;
String consumerName = userCC.getDurable();
if (consumerName == null) {
consumerName = userCC.getName();
}
String inboxDeliver = userCC.getDeliverSubject();
// 4. Does this consumer already exist? FastBind bypasses the lookup;
// the dev better know what they are doing...
if (!so.isFastBind() && consumerName != null) {
ConsumerInfo serverInfo = lookupConsumerInfo(settledStream, consumerName);
if (serverInfo != null) { // the consumer for that durable already exists
serverCC = serverInfo.getConsumerConfiguration();
// check to see if the user sent a different version than the server has
// because modifications are not allowed during create subscription
ConsumerConfigurationComparer userCCC = new ConsumerConfigurationComparer(userCC);
List changes = userCCC.getChanges(serverCC);
if (!changes.isEmpty()) {
throw JsSubExistingConsumerCannotBeModified.instance("Changed fields: " + changes);
}
// deliver subject must be null/empty for pull, defined for push
if (isPullMode) {
if (!nullOrEmpty(serverCC.getDeliverSubject())) {
throw JsSubConsumerAlreadyConfiguredAsPush.instance();
}
}
else if (nullOrEmpty(serverCC.getDeliverSubject())) {
throw JsSubConsumerAlreadyConfiguredAsPull.instance();
}
if (serverCC.getDeliverGroup() == null) {
// lookedUp was null, means existing consumer is not a queue consumer
if (settledDeliverGroup == null) {
// ok fine, no queue requested and the existing consumer is also not a queue consumer
// we must check if the consumer is in use though
if (serverInfo.isPushBound()) {
throw JsSubConsumerAlreadyBound.instance();
}
}
else { // else they requested a queue but this durable was not configured as queue
throw JsSubExistingConsumerNotQueue.instance();
}
}
else if (settledDeliverGroup == null) {
throw JsSubExistingConsumerIsQueue.instance();
}
else if (!serverCC.getDeliverGroup().equals(settledDeliverGroup)) {
throw JsSubExistingQueueDoesNotMatchRequestedQueue.instance();
}
// consumer already exists, make sure the filter subject matches
// subscribeSubject, if supplied came from the user directly
// or in the userCC or might not have been in either place
if (settledFilterSubjects.isEmpty()) {
// still also might be null, which the server treats as >
if (serverCC.getFilterSubjects() != null) {
settledFilterSubjects = serverCC.getFilterSubjects();
}
}
else if (!consumerFilterSubjectsAreEquivalent(settledFilterSubjects, serverCC.getFilterSubjects())) {
throw JsSubSubjectDoesNotMatchFilter.instance();
}
inboxDeliver = serverCC.getDeliverSubject(); // use the deliver subject as the inbox. It may be null, that's ok, we'll fix that later
}
else if (so.isBind()) {
throw JsSubConsumerNotFoundRequiredInBind.instance();
}
}
// 5. If pull or no deliver subject (inbox) provided or found, make an inbox.
final String settledInboxDeliver;
if (isPullMode) {
settledInboxDeliver = conn.createInbox() + ".*";
}
else if (inboxDeliver == null) {
settledInboxDeliver = conn.createInbox();
}
else {
settledInboxDeliver = inboxDeliver;
}
// 6. If consumer does not exist, create and settle on the config. Name will have to wait
// If the consumer exists, I know what the settled info is
final ConsumerConfiguration settledCC;
final String settledConsumerName;
if (so.isFastBind() || serverCC != null) {
settledCC = serverCC;
settledConsumerName = so.getName(); // will never be null in this case
}
else {
ConsumerConfiguration.Builder ccBuilder = ConsumerConfiguration.builder(userCC);
// Pull mode doesn't maintain a deliver subject. It's actually an error if we send it.
if (!isPullMode) {
ccBuilder.deliverSubject(settledInboxDeliver);
}
// userCC.filterSubjects might have originally been empty
// but there might have been a userSubscribeSubject,
// so this makes sure it's resolved either way
ccBuilder.filterSubjects(settledFilterSubjects);
ccBuilder.deliverGroup(settledDeliverGroup);
settledCC = ccBuilder.build();
settledConsumerName = null; // the server will give us a name
}
// 7. create the subscription. lambda needs final or effectively final vars
final MessageManager mm;
final NatsSubscriptionFactory subFactory;
if (isPullMode) {
if (pmmInstance == null) {
MessageManagerFactory mmFactory = so.isOrdered() ? _pullOrderedMessageManagerFactory : _pullMessageManagerFactory;
mm = mmFactory.createMessageManager(conn, this, settledStream, so, settledCC, false, dispatcher == null);
}
else {
mm = pmmInstance;
}
subFactory = (sid, lSubject, lQgroup, lConn, lDispatcher)
-> new NatsJetStreamPullSubscription(sid, lSubject, lConn, lDispatcher, this, settledStream, settledConsumerName, mm);
}
else {
MessageManagerFactory mmFactory = so.isOrdered() ? _pushOrderedMessageManagerFactory : _pushMessageManagerFactory;
mm = mmFactory.createMessageManager(conn, this, settledStream, so, settledCC, settledDeliverGroup != null, dispatcher == null);
subFactory = (sid, lSubject, lQgroup, lConn, lDispatcher) -> {
NatsJetStreamSubscription nsub = new NatsJetStreamSubscription(sid, lSubject, lQgroup, lConn, lDispatcher,
this, settledStream, settledConsumerName, mm);
if (lDispatcher == null) {
nsub.setPendingLimits(so.getPendingMessageLimit(), so.getPendingByteLimit());
}
return nsub;
};
}
NatsJetStreamSubscription sub;
if (dispatcher == null) {
sub = (NatsJetStreamSubscription) conn.createSubscription(settledInboxDeliver, settledDeliverGroup, null, subFactory);
}
else {
AsyncMessageHandler handler = new AsyncMessageHandler(mm, userHandler, isAutoAck, settledCC);
sub = (NatsJetStreamSubscription) dispatcher.subscribeImplJetStream(settledInboxDeliver, settledDeliverGroup, handler, subFactory);
}
// 8. The consumer might need to be created, do it here
if (settledConsumerName == null) {
_createConsumerUnsubscribeOnException(settledStream, settledCC, sub);
}
return sub;
}
static class ConsumerConfigurationComparer extends ConsumerConfiguration {
public ConsumerConfigurationComparer(ConsumerConfiguration cc) {
super(cc);
}
public List getChanges(ConsumerConfiguration serverCc) {
ConsumerConfigurationComparer serverCcc = new ConsumerConfigurationComparer(serverCc);
List changes = new ArrayList<>();
if (deliverPolicy != null && deliverPolicy != serverCcc.getDeliverPolicy()) { changes.add("deliverPolicy"); }
if (ackPolicy != null && ackPolicy != serverCcc.getAckPolicy()) { changes.add("ackPolicy"); }
if (replayPolicy != null && replayPolicy != serverCcc.getReplayPolicy()) { changes.add("replayPolicy"); }
if (flowControl != null && flowControl != serverCcc.isFlowControl()) { changes.add("flowControl"); }
if (headersOnly != null && headersOnly != serverCcc.isHeadersOnly()) { changes.add("headersOnly"); }
if (memStorage != null && memStorage != serverCcc.isMemStorage()) { changes.add("memStorage"); }
if (startSeq != null && !startSeq.equals(serverCcc.getStartSequence())) { changes.add("startSequence"); }
if (rateLimit != null && !rateLimit.equals(serverCcc.getRateLimit())) { changes.add("rateLimit"); }
if (maxDeliver != null && maxDeliver != serverCcc.getMaxDeliver()) { changes.add("maxDeliver"); }
if (maxAckPending != null && maxAckPending != serverCcc.getMaxAckPending()) { changes.add("maxAckPending"); }
if (maxPullWaiting != null && maxPullWaiting != serverCcc.getMaxPullWaiting()) { changes.add("maxPullWaiting"); }
if (maxBatch != null && maxBatch != serverCcc.getMaxBatch()) { changes.add("maxBatch"); }
if (maxBytes != null && maxBytes != serverCcc.getMaxBytes()) { changes.add("maxBytes"); }
if (numReplicas != null && !numReplicas.equals(serverCcc.numReplicas)) { changes.add("numReplicas"); }
if (pauseUntil != null && !pauseUntil.equals(serverCcc.pauseUntil)) { changes.add("pauseUntil"); }
if (ackWait != null && !ackWait.equals(getOrUnset(serverCcc.ackWait))) { changes.add("ackWait"); }
if (idleHeartbeat != null && !idleHeartbeat.equals(getOrUnset(serverCcc.idleHeartbeat))) { changes.add("idleHeartbeat"); }
if (maxExpires != null && !maxExpires.equals(getOrUnset(serverCcc.maxExpires))) { changes.add("maxExpires"); }
if (inactiveThreshold != null && !inactiveThreshold.equals(getOrUnset(serverCcc.inactiveThreshold))) { changes.add("inactiveThreshold"); }
if (startTime != null && !startTime.equals(serverCcc.startTime)) { changes.add("startTime"); }
if (description != null && !description.equals(serverCcc.description)) { changes.add("description"); }
if (sampleFrequency != null && !sampleFrequency.equals(serverCcc.sampleFrequency)) { changes.add("sampleFrequency"); }
if (deliverSubject != null && !deliverSubject.equals(serverCcc.deliverSubject)) { changes.add("deliverSubject"); }
if (deliverGroup != null && !deliverGroup.equals(serverCcc.deliverGroup)) { changes.add("deliverGroup"); }
if (backoff != null && !consumerFilterSubjectsAreEquivalent(backoff, serverCcc.backoff)) { changes.add("backoff"); }
if (metadata != null && !mapsAreEquivalent(metadata, serverCcc.metadata)) { changes.add("metadata"); }
if (filterSubjects != null && !consumerFilterSubjectsAreEquivalent(filterSubjects, serverCcc.filterSubjects)) { changes.add("filterSubjects"); }
// do not need to check Durable because the original is retrieved by the durable name
return changes;
}
}
static class AsyncMessageHandler implements MessageHandler {
MessageManager manager;
MessageHandler userHandler;
boolean autoAck;
public AsyncMessageHandler(MessageManager manager, MessageHandler userHandler, boolean isAutoAck, ConsumerConfiguration settledServerCC) {
this.manager = manager;
this.userHandler = userHandler;
autoAck = isAutoAck && settledServerCC.getAckPolicy() != AckPolicy.None;
}
@Override
public void onMessage(Message msg) throws InterruptedException {
if (manager.manage(msg) == ManageResult.MESSAGE) {
userHandler.onMessage(msg);
if (autoAck) {
msg.ack();
}
}
}
}
private String lookupStreamSubject(String stream) throws IOException, JetStreamApiException {
StreamInfo si = _getStreamInfo(stream, null);
List streamSubjects = si.getConfiguration().getSubjects();
return streamSubjects.size() == 1 ? streamSubjects.get(0) : null;
}
/**
* {@inheritDoc}
*/
@Override
public JetStreamSubscription subscribe(String subscribeSubject) throws IOException, JetStreamApiException {
subscribeSubject = validateSubject(subscribeSubject, true);
return createSubscription(subscribeSubject, null, null, null, null, null, false, null);
}
/**
* {@inheritDoc}
*/
@Override
public JetStreamSubscription subscribe(String subscribeSubject, PushSubscribeOptions options) throws IOException, JetStreamApiException {
subscribeSubject = validateSubject(subscribeSubject, false);
return createSubscription(subscribeSubject, options, null, null, null, null, false, null);
}
/**
* {@inheritDoc}
*/
@Override
public JetStreamSubscription subscribe(String subscribeSubject, String queue, PushSubscribeOptions options) throws IOException, JetStreamApiException {
subscribeSubject = validateSubject(subscribeSubject, false);
validateQueueName(queue, false);
return createSubscription(subscribeSubject, options, null, queue, null, null, false, null);
}
/**
* {@inheritDoc}
*/
@Override
public JetStreamSubscription subscribe(String subscribeSubject, Dispatcher dispatcher, MessageHandler handler, boolean autoAck) throws IOException, JetStreamApiException {
subscribeSubject = validateSubject(subscribeSubject, false);
validateNotNull(dispatcher, "Dispatcher");
validateNotNull(handler, "Handler");
return createSubscription(subscribeSubject, null, null, null, (NatsDispatcher) dispatcher, handler, autoAck, null);
}
/**
* {@inheritDoc}
*/
@Override
public JetStreamSubscription subscribe(String subscribeSubject, Dispatcher dispatcher, MessageHandler handler, boolean autoAck, PushSubscribeOptions options) throws IOException, JetStreamApiException {
subscribeSubject = validateSubject(subscribeSubject, false);
validateNotNull(dispatcher, "Dispatcher");
validateNotNull(handler, "Handler");
return createSubscription(subscribeSubject, options, null, null, (NatsDispatcher) dispatcher, handler, autoAck, null);
}
/**
* {@inheritDoc}
*/
@Override
public JetStreamSubscription subscribe(String subscribeSubject, String queue, Dispatcher dispatcher, MessageHandler handler, boolean autoAck, PushSubscribeOptions options) throws IOException, JetStreamApiException {
subscribeSubject = validateSubject(subscribeSubject, false);
validateQueueName(queue, false);
validateNotNull(dispatcher, "Dispatcher");
validateNotNull(handler, "Handler");
return createSubscription(subscribeSubject, options, null, queue, (NatsDispatcher) dispatcher, handler, autoAck, null);
}
/**
* {@inheritDoc}
*/
@Override
public JetStreamSubscription subscribe(String subscribeSubject, PullSubscribeOptions options) throws IOException, JetStreamApiException {
subscribeSubject = validateSubject(subscribeSubject, false);
validateNotNull(options, "Pull Subscribe Options");
return createSubscription(subscribeSubject, null, options, null, null, null, false, null);
}
/**
* {@inheritDoc}
*/
@Override
public JetStreamSubscription subscribe(String subscribeSubject, Dispatcher dispatcher, MessageHandler handler, PullSubscribeOptions options) throws IOException, JetStreamApiException {
subscribeSubject = validateSubject(subscribeSubject, false);
validateNotNull(dispatcher, "Dispatcher");
validateNotNull(handler, "Handler");
validateNotNull(options, "Pull Subscribe Options");
return createSubscription(subscribeSubject, null, options, null, (NatsDispatcher) dispatcher, handler, false, null);
}
/**
* {@inheritDoc}
*/
@Override
public StreamContext getStreamContext(String streamName) throws IOException, JetStreamApiException {
Validator.validateStreamName(streamName, true);
return getNatsStreamContext(streamName);
}
/**
* {@inheritDoc}
*/
@Override
public ConsumerContext getConsumerContext(String streamName, String consumerName) throws IOException, JetStreamApiException {
Validator.validateStreamName(streamName, true);
Validator.required(consumerName, "Consumer Name");
return getNatsStreamContext(streamName).getConsumerContext(consumerName);
}
private NatsStreamContext getNatsStreamContext(String streamName) throws IOException, JetStreamApiException {
return new NatsStreamContext(streamName, this, conn, jso);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy