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

com.sun.messaging.jmq.jmsclient.FlowControl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2000, 2020 Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2021, 2022 Contributors to the Eclipse Foundation
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package com.sun.messaging.jmq.jmsclient;

import jakarta.jms.*;
import java.io.PrintStream;
import java.util.*;
import java.util.logging.*;

import com.sun.messaging.jmq.jmsclient.resources.*;

/**
 * Class to handle imq protocol flow control.
 *
 * Every connection has its own FlowControl object (thread). Why??? chiaming: answer to the above question:
 *
 * 1. Simplify the implementation. 2. Reduce interference between connections. For example, congestion in one connection
 * does not affect the others. 3. Bugs isolation. Should there is a bug in one connection, the other connections are not
 * affected.
 *
 *
 */
public class FlowControl implements Runnable, Traceable {
    // The current protocol handler
    protected ProtocolHandler protocolHandler = null;
    protected ConnectionImpl connection = null;

    // flag to indicate if the connection is closed
    protected boolean isClosed = false;

    // Thread naming
    protected static final String imqFlowControl = "imqConnectionFlowControl-";
    protected boolean debug = Debug.debug;

    // Collection of all the FlowControlEntries.
    private Hashtable flowControlTable = null;

    // FlowControlEntries ready for sending resumeFlow.
    private Hashtable readyQueue = null;

    private static boolean FLOWCONTROL_DEBUG = Boolean.getBoolean("imq.flowcontrol.debug");

    // This is replaced witn imqPingInterval as debug interval.
    // private static int FLOWCONTROL_DEBUG_INTERVAL =
    // Integer.getInteger("imq.flowcontrol.debug.interval",
    // 3000).intValue();

    private static String FLOWCONTROL_LOG = System.getProperty("imq.flowcontrol.log");

    private static PrintStream fdbg = null;

    // ping interval -- will be initted with connection.getPingInterval().
    private long pingInterval = 30 * 1000;

    private static final Logger connLogger = ConnectionImpl.connectionLogger;

    private static void initFlowControlDebug() {
        if (FLOWCONTROL_DEBUG == false || fdbg != null) {
            return;
        }

        if (FLOWCONTROL_LOG == null) {
            FLOWCONTROL_LOG = "stderr";
        }

        if (FLOWCONTROL_LOG.equals("stdout")) {
            fdbg = System.out;
        } else if (FLOWCONTROL_LOG.equals("stderr")) {
            fdbg = System.err;
        } else {
            try {
                java.io.FileOutputStream fos = new java.io.FileOutputStream(FLOWCONTROL_LOG, true);
                fdbg = new PrintStream(fos);
            } catch (java.io.IOException e) {
                fdbg = System.err;
            }
        }
    }

    /**
     * Create a flow control manager thread for a connection.
     */
    public FlowControl(ConnectionImpl conn) {
        this.connection = conn;

        this.pingInterval = conn.getPingInterval();

        protocolHandler = connection.getProtocolHandler();

        readyQueue = new Hashtable();
        flowControlTable = new Hashtable();

        initFlowControlDebug();

        addConnectionFlowControl(conn);
    }

    private void addConnectionFlowControl(ConnectionImpl connection) {
        FlowControlEntry fce = new ConnectionFlowControlEntry(this, connection.getProtocolHandler(), connection.protectMode, connection.flowControlMsgSize,
                connection.flowControlWaterMark);

        flowControlTable.put(connection, fce);

        if (FLOWCONTROL_DEBUG) {
            fdbg.println("Added connection flow control entry : " + connection);
        }
    }

    /*
     * private void removeConnectionFlowControl(ConnectionImpl connection) { flowControlTable.remove(connection); }
     */

    /**
     * This method is called by ReadChannel when it receives a message packet with connection flow control bit set.
     */
    public void requestConnectionFlowResume() {
        requestResume(connection);
    }

    public void messageReceived() {
        messageReceived(connection);
    }

    public void messageDelivered() {
        messageDelivered(connection);
    }

    public void addConsumerFlowControl(Consumer consumer) {
        FlowControlEntry fce = new ConsumerFlowControlEntry(this, consumer.getConnection().getProtocolHandler(), consumer);

        flowControlTable.put(consumer, fce);

        if (FLOWCONTROL_DEBUG) {
            fdbg.println("Added flow control entry : " + fce);
        }
    }

    public void removeConsumerFlowControl(Consumer consumer) {
        FlowControlEntry fce = (FlowControlEntry) flowControlTable.remove(consumer);

        if (FLOWCONTROL_DEBUG) {
            fdbg.println("Removed flow control entry : " + fce);
        }
    }

    public void requestResume(Object key) {
        if (debug) {
            Debug.println("**** In requestResume. key = " + key);
        }

        FlowControlEntry fce = getFlowControlEntry(key);

        if (FLOWCONTROL_DEBUG) {
            fdbg.println("PAUSED MESSAGE DELIVERY FOR : " + fce);
        }

        if (connLogger.isLoggable(Level.FINEST)) {
            connLogger.log(Level.FINEST, ClientResources.I_FLOW_CONTROL_PAUSED, fce);
        }

        fce.setResumeRequested(true);
    }

    public void messageReceived(Object key) {
        FlowControlEntry fce = findFlowControlEntry(key);
        if (fce != null) {
            fce.messageReceived();
        }
    }

    public void messageDelivered(Object key) {
        FlowControlEntry fce = findFlowControlEntry(key);
        if (fce != null) {
            fce.messageDelivered();
        }
    }

    // bug 6271876 -- connection flow control
    public void resetFlowControl(Object key, int count) {
        FlowControlEntry fce = findFlowControlEntry(key);
        if (fce != null) {
            fce.resetFlowControl(count);
        }
    }

    public Hashtable getDebugState(Object key) {
        FlowControlEntry fce = findFlowControlEntry(key);
        if (fce != null) {
            return fce.getDebugState();
        }

        return new Hashtable();
    }

    /**
     * Test instrumentation method.
     */
    public Object TEST_GetAttribute(String name, Object key) {
        FlowControlEntry fce = findFlowControlEntry(key);
        return fce.TEST_GetAttribute(name);
    }

    private FlowControlEntry getFlowControlEntry(Object key) {
        FlowControlEntry fce = (FlowControlEntry) flowControlTable.get(key);

        if (fce == null) {
            // This should never happen.
            if (!(key instanceof Consumer) && !(key instanceof ConnectionImpl)) {
                throw new IllegalArgumentException("getFlowControlEntry: Bad key type. key = " + key);
            }

            throw new java.lang.IllegalStateException("FlowControlEntry not found. key = " + key);
        }

        return fce;
    }

    private FlowControlEntry findFlowControlEntry(Object key) {
        if (key == null) {
            return null;
        }

        FlowControlEntry fce = (FlowControlEntry) flowControlTable.get(key);
        return fce;
    }

    /**
     * Start imq flow control thread for this connection.
     */
    public void start() {
        Thread thread = new Thread(this);
        if (connection.hasDaemonThreads()) {
            thread.setDaemon(true);
        }
        // thread.setName(imqFlowControl + connection.getConnectionID());
        thread.setName(imqFlowControl + connection.getLocalID());
        thread.start();
    }

    /**
     * This method exit only if ReadChannel is closed (connection is closed).
     */
    @Override
    public void run() {
        long lastDump = 0;
        while (true) {
            FlowControlEntry[] rqCopy = null;

            // Wait for something to happen.
            synchronized (this) {
                while (!isClosed && readyQueue.isEmpty()) {

                    try {

                        wait(pingInterval);
                        /**
                         * check if this is a timeout wait.
                         */
                        if (isClosed == false && readyQueue.isEmpty()) {
                            /**
                             * send ping if no activities.
                             */
                            if (protocolHandler.getTimeToPing()) {
                                pingBroker();
                            }

                            protocolHandler.setTimeToPing(true);

                        }
                    } catch (InterruptedException e) {
                        if (debug) {
                            Debug.printStackTrace(e);
                        }
                    }

                    // if (FLOWCONTROL_DEBUG &&
                    // FLOWCONTROL_DEBUG_INTERVAL > 0 &&
                    // ((System.currentTimeMillis() - lastDump) >
                    // FLOWCONTROL_DEBUG_INTERVAL)) {
                    if (FLOWCONTROL_DEBUG) {

                        if ((System.currentTimeMillis() - lastDump) > pingInterval) {

                            status_report();

                            lastDump = System.currentTimeMillis();
                        }
                    } // debug

                } // while

                if (isClosed)
                 {
                    break; // Returns from the run() method.
                }

                rqCopy = (FlowControlEntry[]) readyQueue.values().toArray(new FlowControlEntry[readyQueue.size()]);
            }

            // Send resume flow packets.
            try {
                for (int i = 0; i < rqCopy.length; i++) {
                    rqCopy[i].sendResumeFlow();

                    if (FLOWCONTROL_DEBUG) {
                        fdbg.println("SENDING RESUME_FLOW FOR : " + rqCopy[i]);
                    }

                    if (connLogger.isLoggable(Level.FINEST)) {
                        connLogger.log(Level.FINEST, ClientResources.I_FLOW_CONTROL_RESUME, rqCopy[i]);
                    }
                }
            } catch (Exception e) {
                if (connection.isClosed) {
                    isClosed = true;
                    break; // Returns from the run() method.
                } else {
                    Debug.printStackTrace(e);
                }
            }
        }
    }

    private void pingBroker() {

        try {
            // bug 6155026 - client sends PING before authenticated.
            // we decided to have broker ignore PING.
            // if ( connection.isClosed == false ) {
            protocolHandler.ping();
            // }
        } catch (JMSException e) {
            if (debug) {
                Debug.printStackTrace(e);
            }
        }
    }

    private void status_report() {
        fdbg.println("debug_interval = " + pingInterval);
        fdbg.println("\n-------------------------------- " + this + " : " + new java.util.Date());

        Enumeration enum2 = flowControlTable.elements();

        while (enum2.hasMoreElements()) {
            FlowControlEntry fce = (FlowControlEntry) enum2.nextElement();
            fce.status_report(fdbg);
        }
    }

    protected synchronized void addToReadyQueue(FlowControlEntry fce) {
        if (debug) {
            Debug.println("In addToReadyQueue : " + fce);
        }
        readyQueue.put(fce, fce);
        notifyAll();
    }

    protected synchronized void removeFromReadyQueue(FlowControlEntry fce) {
        if (debug) {
            Debug.println("In addToReadyQueue : " + fce);
        }

        readyQueue.remove(fce);
    }

    /**
     * ReadChannel calls this method when connection is closed.
     */
    public synchronized void close() {
        isClosed = true;
        notifyAll();
    }

    @Override
    public void dump(PrintStream ps) {
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy