All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.nats.streaming.SubscriptionImpl Maven / Gradle / Ivy

/*
 *  Copyright (c) 2015-2016 Apcera Inc. All rights reserved. This program and the accompanying
 *  materials are made available under the terms of the MIT License (MIT) which accompanies this
 *  distribution, and is available at http://opensource.org/licenses/MIT
 */

package io.nats.streaming;

import static io.nats.streaming.NatsStreaming.ERR_CLOSE_REQ_TIMEOUT;
import static io.nats.streaming.NatsStreaming.ERR_NO_SERVER_SUPPORT;
import static io.nats.streaming.NatsStreaming.ERR_UNSUB_REQ_TIMEOUT;
import static io.nats.streaming.NatsStreaming.PFX;

import io.nats.client.Connection;
import io.nats.streaming.protobuf.SubscriptionResponse;
import io.nats.streaming.protobuf.UnsubscribeRequest;
import java.io.IOException;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class SubscriptionImpl implements Subscription {

    static final long DEFAULT_ACK_WAIT = 30 * 1000;
    static final int DEFAULT_MAX_IN_FLIGHT = 1024;

    private final ReadWriteLock rwlock = new ReentrantReadWriteLock();
    StreamingConnectionImpl sc;
    private String subject;
    private String qgroup;
    String inbox;
    String ackInbox;
    io.nats.client.Subscription inboxSub;
    SubscriptionOptions opts = new SubscriptionOptions.Builder().build();
    MessageHandler cb;

    SubscriptionImpl() {
    }

    SubscriptionImpl(String subject, String qgroup, MessageHandler cb,
                     StreamingConnectionImpl sc,
                     SubscriptionOptions opts) {
        this.subject = subject;
        this.qgroup = qgroup;
        this.cb = cb;
        this.sc = sc;
        if (opts != null) {
            this.opts = opts;
        }
        this.inbox = sc.newInbox();
    }

    void rLock() {
        rwlock.readLock().lock();
    }

    void rUnlock() {
        rwlock.readLock().unlock();
    }

    void wLock() {
        rwlock.writeLock().lock();
    }

    void wUnlock() {
        rwlock.writeLock().unlock();
    }

    String getAckInbox() {
        return this.ackInbox;
    }

    StreamingConnectionImpl getConnection() {
        return this.sc;
    }

    String getInbox() {
        return this.inbox;
    }

    MessageHandler getMessageHandler() {
        return this.cb;
    }

    @Override
    public String getQueue() {
        return this.qgroup;
    }

    @Override
    public String getSubject() {
        return this.subject;
    }

    @Override
    public SubscriptionOptions getOptions() {
        return this.opts;
    }

    @Override
    public void close() throws IOException {
        if (this.sc == null) {
            // already closed
            return;
        }
        close(true);
    }

    @Override
    public void close(boolean unsubscribe) throws IOException {
        StreamingConnectionImpl sc;
        String reqSubject;
        Connection nc;
        wLock();
        try {
            sc = this.sc;
            if (sc == null) {
                throw new IllegalStateException(NatsStreaming.ERR_BAD_SUBSCRIPTION);
            }
            this.sc = null;
            if (inboxSub != null) {
                try {
                    inboxSub.unsubscribe();
                } catch (Exception e) {
                    // Silently ignore this, we can't do anything about it
                }
                inboxSub = null;
            }
        } finally {
            wUnlock();
        }

        if (sc == null) {
            throw new IllegalStateException(NatsStreaming.ERR_BAD_SUBSCRIPTION);
        }

        sc.lock();
        try {
            if (sc.nc == null) {
                throw new IllegalStateException(NatsStreaming.ERR_CONNECTION_CLOSED);
            }

            sc.subMap.remove(this.inbox);
            reqSubject = sc.unsubRequests;
            if (!unsubscribe) {
                reqSubject = sc.subCloseRequests;
                if (reqSubject.isEmpty()) {
                    throw new IllegalStateException(ERR_NO_SERVER_SUPPORT);
                }
            }
            // Snapshot connection to avoid data race, since the connection may be
            // closing while we try to send the request
            nc = sc.getNatsConnection();
        } finally {
            sc.unlock();
        }

        byte[] bytes;

        UnsubscribeRequest usr = UnsubscribeRequest.newBuilder()
                .setClientID(sc.getClientId()).setSubject(subject).setInbox(ackInbox).build();
        bytes = usr.toByteArray();

        io.nats.client.Message reply;

        try {
            reply = nc.request(reqSubject, bytes, sc.opts.connectTimeout.toMillis());
            if (reply == null) {
                if (unsubscribe) {
                    throw new IOException(ERR_UNSUB_REQ_TIMEOUT);
                }
                throw new IOException(ERR_CLOSE_REQ_TIMEOUT);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException(e);
        }

        SubscriptionResponse response = SubscriptionResponse.parseFrom(reply.getData());
        if (!response.getError().isEmpty()) {
            throw new IOException(PFX + response.getError());
        }
    }

    @Override
    public void unsubscribe() throws IOException {
        close(true);
    }

    void setAckInbox(String ackInbox) {
        this.ackInbox = ackInbox;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy