/*
 *
 * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
// Copyright (c) 1995-96 by Cisco Systems, Inc.

package com.sun.jmx.snmp.daemon;

import java.util.Enumeration;
import java.util.logging.Level;
import java.util.Vector;

// import debug stuff
//
import static com.sun.jmx.defaults.JmxProperties.SNMP_ADAPTOR_LOGGER;

/**
 * This class starts a thread which picks up a session from the queue
 * and prepares the inform request protocol data unit (PDU) packet and sends
 * it to the manager. The request is then added to the wait queue and
 * marked as one that is waiting for a response.
 */

final class SnmpSendServer extends Thread {

        // VARIABLES
    //----------

        private int intervalRange = 5 * 1000 ;
        private Vector readyPool ;

    SnmpQManager snmpq = null ;

    // This boolean is used to stop handling requests while the corresponding SnmpQManager
    // is being destroyed.
    //
    boolean isBeingDestroyed = false;

    // CONSTRUCTORS
    //-------------

    public SnmpSendServer(ThreadGroup grp, SnmpQManager q) {
                super(grp, "SnmpSendServer") ;
                snmpq = q ;
                start() ;
        }

    public synchronized void stopSendServer() {

        if (isAlive()) {
            interrupt();
            try {
                // Wait until the thread die.
                //
                join();
            } catch (InterruptedException e) {
                // Ignore...
            }
        }
    }

        public void run() {
            Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

            if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
                SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSendServer.class.getName(),
                    "run", "Thread Started");
            }
            while (true) {
                try {
                    prepareAndSendRequest() ;
                    if (isBeingDestroyed == true)
                        break;
                } catch (Exception anye) {
                    if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
                        SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSendServer.class.getName(),
                            "run", "Exception in send server", anye);
                    }
                } catch (ThreadDeath td) {
                    // This is not good but Netscape does kill all threads when
                    // the pagecontext changes.
                    if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
                        SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSendServer.class.getName(),
                            "run", "Exiting... Fatal error");
                    }
                    throw td ;
                } catch (OutOfMemoryError ome) {
                    if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
                        SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSendServer.class.getName(),
                            "run", "Out of memory");
                    }
                } catch (Error err) {
                    if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
                        SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSendServer.class.getName(),
                            "run", "Got unexpected error", err);
                    }
                    throw err ;
                }
            }
        }

    private void prepareAndSendRequest() {

        if (readyPool == null || readyPool.isEmpty()) {
            // wait to be signaled by the an active request.
            if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
                SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSendServer.class.getName(),
                    "prepareAndSendRequest", "Blocking for inform requests");
            }
            readyPool = snmpq.getAllOutstandingRequest(intervalRange) ;
            if (isBeingDestroyed == true)
                return;
        } else {
            if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
                SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpSendServer.class.getName(),
                    "prepareAndSendRequest", "Inform requests from a previous block left unprocessed. Will try again");
            }
        }

        if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
            SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSendServer.class.getName(),
                "prepareAndSendRequest", "List of inform requests to send : " + reqListToString(readyPool));
        }

        synchronized(this) {
            if (readyPool.size() < 2) {
                // Fire all requests as independent requests.
                fireRequestList(readyPool) ;
                return ;
            }

            while (!readyPool.isEmpty()) {
                SnmpInformRequest req = (SnmpInformRequest) readyPool.lastElement() ;
                if (req != null && req.inProgress()) {
                    fireRequest(req) ;
                }
                readyPool.removeElementAt(readyPool.size() - 1) ;
            }
            readyPool.removeAllElements() ;
        }
    }

        /**
         * This will fire the specified request.
         */
        void fireRequest(SnmpInformRequest req) {
                if (req != null && req.inProgress())  {
                    if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
                        SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpSendServer.class.getName(),
                            "fireRequest", "Firing inform request directly. -> " + req.getRequestId());
                    }
                    req.action() ;
                }
        }

    void fireRequestList(Vector reqList) {
        // Fire all requests as independent requests.
        while (!reqList.isEmpty()) {
            SnmpInformRequest req = (SnmpInformRequest) reqList.lastElement() ;
            if (req != null && req.inProgress())
                fireRequest(req) ;
            reqList.removeElementAt(reqList.size() - 1) ;
        }
    }

        final String reqListToString(Vector vec) {
                StringBuffer s = new StringBuffer(vec.size() * 100) ;

                Enumeration dbge = vec.elements() ;
                while (dbge.hasMoreElements()) {
                        SnmpInformRequest reqc = (SnmpInformRequest) dbge.nextElement() ;
            s.append("InformRequestId -> ") ;
                        s.append(reqc.getRequestId()) ;
            s.append(" / Destination -> ") ;
                        s.append(reqc.getAddress()) ;
            s.append(". ") ;
                }
                String str = s.toString() ;
                s = null ;
                return str ;
        }

}
