Package com.sun.jmx.snmp.daemon

Source Code of com.sun.jmx.snmp.daemon.CommunicatorServer

/*
* @(#)file      CommunicatorServer.java
* @(#)author    Sun Microsystems, Inc.
* @(#)version   1.60
* @(#)lastedit      05/11/17
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*/


package com.sun.jmx.snmp.daemon;



// java import
//
import java.io.ObjectInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Vector;
import java.util.Enumeration;

// jmx import
//
import javax.management.MBeanServer;
import javax.management.MBeanRegistration;
import javax.management.ObjectName;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationBroadcasterSupport;
import javax.management.MBeanNotificationInfo;
import javax.management.AttributeChangeNotification;
import javax.management.ListenerNotFoundException;
import javax.management.loading.ClassLoaderRepository;
import javax.management.MBeanServerFactory;

// jmx RI import
//
import com.sun.jmx.trace.Trace;
import java.util.NoSuchElementException;

// JSR 160 import
//
// XXX Revisit:
//   used to import com.sun.jmx.snmp.MBeanServerForwarder
// Now using JSR 160 instead. => this is an additional
// dependency to JSR 160.
//
import javax.management.remote.MBeanServerForwarder;

/**
* Defines generic behavior for the server part of a connector or an adaptor.
* Most connectors or adaptors extend <CODE>CommunicatorServer</CODE>
* and inherit this behavior. Connectors or adaptors that do not fit into
* this model do not extend <CODE>CommunicatorServer</CODE>.
* <p>
* A <CODE>CommunicatorServer</CODE> is an active object, it listens for
* client requests  and processes them in its own thread. When necessary, a
* <CODE>CommunicatorServer</CODE> creates other threads to process multiple
* requests concurrently.
* <p>
* A <CODE>CommunicatorServer</CODE> object can be stopped by calling the
* <CODE>stop</CODE> method. When it is stopped, the
* <CODE>CommunicatorServer</CODE> no longer listens to client requests and
* no longer holds any thread or communication resources.
* It can be started again by calling the <CODE>start</CODE> method.
* <p>
* A <CODE>CommunicatorServer</CODE> has a <CODE>State</CODE> attribute
* which reflects its  activity.
* <p>
* <TABLE>
* <TR><TH>CommunicatorServer</TH>      <TH>State</TH></TR>
* <TR><TD><CODE>stopped</CODE></TD>    <TD><CODE>OFFLINE</CODE></TD></TR>
* <TR><TD><CODE>starting</CODE></TD>    <TD><CODE>STARTING</CODE></TD></TR>
* <TR><TD><CODE>running</CODE></TD>     <TD><CODE>ONLINE</CODE></TD></TR>
* <TR><TD><CODE>stopping</CODE></TD>     <TD><CODE>STOPPING</CODE></TD></TR>
* </TABLE>
* <p>
* The <CODE>STARTING</CODE> state marks the transition
* from <CODE>OFFLINE</CODE> to <CODE>ONLINE</CODE>.
* <p>
* The <CODE>STOPPING</CODE> state marks the transition from
* <CODE>ONLINE</CODE> to <CODE>OFFLINE</CODE>. This occurs when the
* <CODE>CommunicatorServer</CODE> is finishing or interrupting active
* requests.
* <p>
* When a <CODE>CommunicatorServer</CODE> is unregistered from the MBeanServer,
* it is stopped automatically.
* <p>
* When the value of the <CODE>State</CODE> attribute changes the
* <CODE>CommunicatorServer</CODE> sends a
* <tt>{@link javax.management.AttributeChangeNotification}</tt> to the
* registered listeners, if any.
*
* <p><b>This API is a Sun Microsystems internal API  and is subject
* to change without notice.</b></p>
* @version     1.60     11/17/05
* @author      Sun Microsystems, Inc
*/

public abstract class CommunicatorServer
    implements Runnable, MBeanRegistration, NotificationBroadcaster,
         CommunicatorServerMBean {

    //
    // States of a CommunicatorServer
    //

    /**
     * Represents an <CODE>ONLINE</CODE> state.
     */
    public static final int ONLINE = 0 ;

    /**
     * Represents an <CODE>OFFLINE</CODE> state.
     */
    public static final int OFFLINE = 1 ;

    /**
     * Represents a <CODE>STOPPING</CODE> state.
     */
    public static final int STOPPING = 2 ;

    /**
     * Represents a <CODE>STARTING</CODE> state.
     */
    public static final int STARTING = 3 ;

    //
    // Types of connectors.
    //

    /**
     * Indicates that it is an RMI connector type.
     */
    //public static final int RMI_TYPE = 1 ;

    /**
     * Indicates that it is an HTTP connector type.
     */
    //public static final int HTTP_TYPE = 2 ;

    /**
     * Indicates that it is an HTML connector type.
     */
    //public static final int HTML_TYPE = 3 ;

    /**
     * Indicates that it is an SNMP connector type.
     */
    public static final int SNMP_TYPE = 4 ;

    /**
     * Indicates that it is an HTTPS connector type.
     */
    //public static final int HTTPS_TYPE = 5 ;

    //
    // Package variables
    //

    /**
     * The state of the connector server.
     */
     transient volatile int state = OFFLINE ;

    /**
     * The object name of the connector server.
     * @serial
     */
    ObjectName objectName ;

    MBeanServer topMBS;
    MBeanServer bottomMBS;

    /**
     */
    transient String dbgTag = null ;

    /**
     * The maximum number of clients that the CommunicatorServer can
     * process concurrently.
     * @serial
     */
    int maxActiveClientCount = 1 ;

    /**
     */
    transient int servedClientCount = 0 ;

    /**
     * The host name used by this CommunicatorServer.
     * @serial
     */
    String host = null ;

    /**
     * The port number used by this CommunicatorServer.
     * @serial
     */
    int port = -1 ;


    //
    // Private fields
    //

    /* This object controls access to the "state" and "interrupted" variables.
       If held at the same time as the lock on "this", the "this" lock must
       be taken first.  */
    private transient Object stateLock = new Object();

    private transient Vector<ClientHandler>
            clientHandlerVector = new Vector<ClientHandler>() ;

    private transient Thread fatherThread = Thread.currentThread() ;
    private transient Thread mainThread = null ;

    private volatile boolean stopRequested = false ;
    private boolean interrupted = false;
    private transient Exception startException = null;

    // Notifs count, broadcaster and info
    private transient long notifCount = 0;
    private transient NotificationBroadcasterSupport notifBroadcaster =
  new NotificationBroadcasterSupport();
    private transient MBeanNotificationInfo[] notifInfos = null;
   

    /**
     * Instantiates a <CODE>CommunicatorServer</CODE>.
     *
     * @param connectorType Indicates the connector type. Possible values are:
     * SNMP_TYPE.
     *
     * @exception <CODE>java.lang.IllegalArgumentException</CODE>
     *            This connector type is not correct.
     */ 
    public CommunicatorServer(int connectorType)
  throws IllegalArgumentException {
        switch (connectorType) {
        case SNMP_TYPE :
            infoType = Trace.INFO_ADAPTOR_SNMP ;
            break;
        default:
            throw new IllegalArgumentException("Invalid connector Type") ;
        }
        dbgTag = makeDebugTag() ;
    }

    protected Thread createMainThread() {
  return new Thread (this, makeThreadName());
    }

    /**
     * Starts this <CODE>CommunicatorServer</CODE>.
     * <p>
     * Has no effect if this <CODE>CommunicatorServer</CODE> is
     * <CODE>ONLINE</CODE> or <CODE>STOPPING</CODE>.
     * @param timeout Time in ms to wait for the connector to start.
     *        If <code>timeout</code> is positive, wait for at most
     *        the specified time. An infinite timeout can be specified
     *        by passing a <code>timeout</code> value equals
     *        <code>Long.MAX_VALUE</code>. In that case the method
     *        will wait until the connector starts or fails to start.
     *        If timeout is negative or zero, returns as soon as possible
     *        without waiting.
     * @exception CommunicationException if the connectors fails to start.
     * @exception InterruptedException if the thread is interrupted or the
     *            timeout expires.
     */
    public void start(long timeout)
  throws CommunicationException, InterruptedException {
  boolean start;

  synchronized (stateLock) {
      if (state == STOPPING) {
    // Fix for bug 4352451:
    //     "java.net.BindException: Address in use".
    waitState(OFFLINE, 60000);
      }
      start = (state == OFFLINE);
      if (start) {
    changeState(STARTING);
    stopRequested = false;
    interrupted = false;
    startException = null;
      }
  }

  if (!start) {
            if (isTraceOn())
                trace("start","Connector is not OFFLINE") ;
      return;
  }

  if (isTraceOn())
      trace("start","--> Start connector ") ;

  mainThread = createMainThread();

  mainThread.start() ;
 
  if (timeout > 0) waitForStart(timeout);
    }
   
    /**
     * Starts this <CODE>CommunicatorServer</CODE>.
     * <p>
     * Has no effect if this <CODE>CommunicatorServer</CODE> is
     * <CODE>ONLINE</CODE> or <CODE>STOPPING</CODE>.
     */
    public void start() {
  try {
      start(0);
  } catch (InterruptedException x) {
      // can not happen because of `0'
      trace("start","interrupted: " + x);
  }
    }
   
    /**
     * Stops this <CODE>CommunicatorServer</CODE>.
     * <p>
     * Has no effect if this <CODE>CommunicatorServer</CODE> is
     * <CODE>OFFLINE</CODE> or  <CODE>STOPPING</CODE>.
     */
    public void stop() {
  synchronized (stateLock) {
      if (state == OFFLINE || state == STOPPING) {
    if (isTraceOn())
        trace("stop","Connector is not ONLINE") ;
    return;
      }
      changeState(STOPPING);
      //
      // Stop the connector thread
      //
      if (isTraceOn())
    trace("stop","Interrupt main thread") ;
      stopRequested = true ;
      if (!interrupted) {       
    interrupted = true;
    mainThread.interrupt();
      }
  }

  //
  // Call terminate on each active client handler
  //
  if (isTraceOn()) {
      trace("stop","terminateAllClient") ;
  }
  terminateAllClient() ;
 
  // ----------------------
  // changeState
  // ----------------------
  synchronized (stateLock) {
      if (state == STARTING)
    changeState(OFFLINE);
  }
    }

    /**
     * Tests whether the <CODE>CommunicatorServer</CODE> is active.
     *
     * @return True if connector is <CODE>ONLINE</CODE>; false otherwise.
     */
    public boolean isActive() {
  synchronized (stateLock) {
      return (state == ONLINE);
  }
    }

    /**
     * <p>Waits until either the State attribute of this MBean equals the
     * specified <VAR>wantedState</VAR> parameter,
     * or the specified  <VAR>timeOut</VAR> has elapsed.
     * The method <CODE>waitState</CODE> returns with a boolean value
     * indicating whether the specified <VAR>wantedState</VAR> parameter
     * equals the value of this MBean's State attribute at the time the method
     * terminates.</p>
     *
     * <p>Two special cases for the <VAR>timeOut</VAR> parameter value are:</p>
     * <UL><LI> if <VAR>timeOut</VAR> is negative then <CODE>waitState</CODE>
     *     returns immediately (i.e. does not wait at all),</LI>
     * <LI> if <VAR>timeOut</VAR> equals zero then <CODE>waitState</CODE>
     *     waits untill the value of this MBean's State attribute
     *     is the same as the <VAR>wantedState</VAR> parameter (i.e. will wait
     *     indefinitely if this condition is never met).</LI></UL>
     *
     * @param wantedState The value of this MBean's State attribute to wait
     *        for. <VAR>wantedState</VAR> can be one of:
     * <ul>
     * <li><CODE>CommunicatorServer.OFFLINE</CODE>,</li>
     * <li><CODE>CommunicatorServer.ONLINE</CODE>,</li>
     * <li><CODE>CommunicatorServer.STARTING</CODE>,</li>
     * <li><CODE>CommunicatorServer.STOPPING</CODE>.</li>
     * </ul>
     * @param timeOut The maximum time to wait for, in milliseconds,
     *        if positive.
     * Infinite time out if 0, or no waiting at all if negative.
     *
     * @return true if the value of this MBean's State attribute is the
     *      same as the <VAR>wantedState</VAR> parameter; false otherwise.
     */
    public boolean waitState(int wantedState, long timeOut) {
        if (isTraceOn())
            trace("waitState", wantedState + "(0on,1off,2st) TO=" + timeOut +
      " ; current state = " + getStateString());

  long endTime = 0;
  if (timeOut > 0)
      endTime = System.currentTimeMillis() + timeOut;

  synchronized (stateLock) {
      while (state != wantedState) {
    if (timeOut < 0) {
        if (isTraceOn())
      trace("waitState", "timeOut < 0, return without wait");
        return false;
    } else {
        try {
      if (timeOut > 0) {
          long toWait = endTime - System.currentTimeMillis();
          if (toWait <= 0) {
        if (isTraceOn())
            trace("waitState", "timed out");
        return false;
          }
          stateLock.wait(toWait);
      } else // timeOut == 0
          stateLock.wait();
      }
        } catch (InterruptedException e) {
      if (isTraceOn())
          trace("waitState", "wait interrupted");
      return (state == wantedState);
        }
    }
      }
      if (isTraceOn())
    trace("waitState", "returning in desired state");
      return true;
  }
    }

    /**
     * <p>Waits until the communicator is started or timeout expires.
     *   
     * @param timeout Time in ms to wait for the connector to start.
     *        If <code>timeout</code> is positive, wait for at most
     *        the specified time. An infinite timeout can be specified
     *        by passing a <code>timeout</code> value equals
     *        <code>Long.MAX_VALUE</code>. In that case the method
     *        will wait until the connector starts or fails to start.
     *        If timeout is negative or zero, returns as soon as possible
     *        without waiting.
     *
     * @exception CommunicationException if the connectors fails to start.
     * @exception InterruptedException if the thread is interrupted or the
     *            timeout expires.
     *
     */
    private void waitForStart(long timeout)
  throws CommunicationException, InterruptedException {
        if (isTraceOn())
            trace("waitForStart", "Timeout=" + timeout +
      " ; current state = " + getStateString());
 
  final long startTime = System.currentTimeMillis();
  synchronized (stateLock) {
      while (state == STARTING) {
    // Time elapsed since startTime...
    //
    final long elapsed = System.currentTimeMillis() - startTime;

    // wait for timeout - elapsed.
    // A timeout of Long.MAX_VALUE is equivalent to something
    // like 292271023 years - which is pretty close to
    // forever as far as we are concerned ;-)
    //
    final long remainingTime = timeout-elapsed;

    // If remainingTime is negative, the timeout has elapsed.
    //
    if (remainingTime < 0) {
        if (isTraceOn())
      trace("waitForStart",
            "timeout < 0, return without wait");
        throw new InterruptedException("Timeout expired");
    }

    // We're going to wait until someone notifies on the
    // the stateLock object, or until the timeout expires,
    // or until the thread is interrupted.
    //
    try {
        stateLock.wait(remainingTime);
    } catch (InterruptedException e) {
        if (isTraceOn())
      trace("waitForStart", "wait interrupted");

        // If we are now ONLINE, then no need to rethrow the
        // exception... we're simply going to exit the while
        // loop. Otherwise, throw the InterruptedException.
        //
        if (state != ONLINE) throw e;
    }
      }

      // We're no longer in STARTING state
      //
      if (state == ONLINE) {
    // OK, we're started, everything went fine, just return
    //
    if (isTraceOn()) trace("waitForStart", "started");
    return;
      } else if (startException instanceof CommunicationException) {
    // There was some exception during the starting phase.
    // Cast and throw...
    //
    throw (CommunicationException)startException;
      } else if (startException instanceof InterruptedException) {
    // There was some exception during the starting phase.
    // Cast and throw...
    //
    throw (InterruptedException)startException;
      } else if (startException != null) {
    // There was some exception during the starting phase.
    // Wrap and throw...
    //
    throw new CommunicationException(startException,
             "Failed to start: "+
             startException);
      } else {
    // We're not ONLINE, and there's no exception...
    // Something went wrong but we don't know what...
    //
    throw new CommunicationException("Failed to start: state is "+
             getStringForState(state));
      }
  }
    }

    /**
     * Gets the state of this <CODE>CommunicatorServer</CODE> as an integer.
     *
     * @return <CODE>ONLINE</CODE>, <CODE>OFFLINE</CODE>,
     *         <CODE>STARTING</CODE> or <CODE>STOPPING</CODE>.
     */
    public int getState() {
  synchronized (stateLock) {
      return state ;
  }
    }

    /**
     * Gets the state of this <CODE>CommunicatorServer</CODE> as a string.
     *
     * @return One of the strings "ONLINE", "OFFLINE", "STARTING" or
     *         "STOPPING".
     */
    public String getStateString() {
        return getStringForState(state) ;
    }

    /**
     * Gets the host name used by this <CODE>CommunicatorServer</CODE>.
     *
     * @return The host name used by this <CODE>CommunicatorServer</CODE>.
     */
    public String getHost() {
        try {
            host = InetAddress.getLocalHost().getHostName();
        } catch (Exception e) {
            host = "Unknown host";
        }
        return host ;
    }

    /**
     * Gets the port number used by this <CODE>CommunicatorServer</CODE>.
     *
     * @return The port number used by this <CODE>CommunicatorServer</CODE>.
     */
    public int getPort() {
  synchronized (stateLock) {
      return port ;
  }
    }

    /**
     * Sets the port number used by this <CODE>CommunicatorServer</CODE>.
     *
     * @param port The port number used by this
     *             <CODE>CommunicatorServer</CODE>.
     *
     * @exception java.lang.IllegalStateException This method has been invoked
     * while the communicator was ONLINE or STARTING.
     */
    public void setPort(int port) throws java.lang.IllegalStateException {
  synchronized (stateLock) {
      if ((state == ONLINE) || (state == STARTING))
    throw new IllegalStateException("Stop server before " +
            "carrying out this operation");
      this.port = port;
      dbgTag = makeDebugTag();
  }
    }

    /**
     * Gets the protocol being used by this <CODE>CommunicatorServer</CODE>.
     * @return The protocol as a string.
     */
    public abstract String getProtocol() ;

    /**
     * Gets the number of clients that have been processed by this
     * <CODE>CommunicatorServer</CODE>  since its creation.
     *
     * @return The number of clients handled by this
     *         <CODE>CommunicatorServer</CODE>
     *         since its creation. This counter is not reset by the
     *         <CODE>stop</CODE> method.
     */
    int getServedClientCount() {
        return servedClientCount ;
    }
 
    /**
     * Gets the number of clients currently being processed by this
     * <CODE>CommunicatorServer</CODE>.
     *
     * @return The number of clients currently being processed by this
     *         <CODE>CommunicatorServer</CODE>.
     */
    int getActiveClientCount() {
        int result = clientHandlerVector.size() ;
        return result ;
    }

    /**
     * Gets the maximum number of clients that this
     * <CODE>CommunicatorServer</CODE> can  process concurrently.
     *
     * @return The maximum number of clients that this
     *         <CODE>CommunicatorServer</CODE> can
     *         process concurrently.
     */
    int getMaxActiveClientCount() {
        return maxActiveClientCount ;
    }

    /**
     * Sets the maximum number of clients this
     * <CODE>CommunicatorServer</CODE> can process concurrently.
     *
     * @param c The number of clients.
     *
     * @exception java.lang.IllegalStateException This method has been invoked
     * while the communicator was ONLINE or STARTING.
     */
    void setMaxActiveClientCount(int c)
  throws java.lang.IllegalStateException {
  synchronized (stateLock) {
      if ((state == ONLINE) || (state == STARTING)) {
    throw new IllegalStateException(
        "Stop server before carrying out this operation");
      }     
      maxActiveClientCount = c ;
  }
    }

    /**
     * For SNMP Runtime internal use only.
     */
    void notifyClientHandlerCreated(ClientHandler h) {
        clientHandlerVector.addElement(h) ;
    }

    /**
     * For SNMP Runtime internal use only.
     */
    synchronized void notifyClientHandlerDeleted(ClientHandler h) {
        clientHandlerVector.removeElement(h);
  notifyAll();
    }

    /**
     * The number of times the communicator server will attempt
     * to bind before giving up.
     **/
    protected int getBindTries() {
  return 50;
    }

    /**
     * The delay, in ms, during which the communicator server will sleep before
     * attempting to bind again.
     **/
    protected long getBindSleepTime() {
  return 100;
    }

    /**
     * For SNMP Runtime internal use only.
     * <p>
     * The <CODE>run</CODE> method executed by this connector's main thread.
     */
    public void run() {
       
        // Fix jaw.00667.B
        // It seems that the init of "i" and "success"
        // need to be done outside the "try" clause...
        // A bug in Java 2 production release ?
        //
        int i = 0;
        boolean success = false;
       
        // ----------------------
        // Bind
        // ----------------------
        try {
            // Fix for bug 4352451: "java.net.BindException: Address in use".
            //
      final int  bindRetries = getBindTries();
      final long sleepTime   = getBindSleepTime();
            while (i < bindRetries && !success) {
                try {
                    // Try socket connection.
                    //
                    doBind();
                    success = true;
                } catch (CommunicationException ce) {
                    i++;
                    try {
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException ie) {
      throw ie;
                    }
                }
            }
            // Retry last time to get correct exception.
            //
            if (!success) {
                // Try socket connection.
                //
                doBind();
            }

        } catch(Exception x) {
            if (isDebugOn()) {
                debug("run","Unexpected exception = "+x) ;
            }
      synchronized(stateLock) {
    startException = x;
    changeState(OFFLINE);
      }
            if (isTraceOn()) {
                trace("run","State is OFFLINE") ;
            }
      doError(x);
      return;
        }

        try {
            // ----------------------
            // State change
            // ----------------------
            changeState(ONLINE) ;
            if (isTraceOn()) {
                trace("run","State is ONLINE") ;
            }

            // ----------------------
            // Main loop
            // ----------------------
            while (!stopRequested) {
                servedClientCount++; 
                doReceive() ;
                waitIfTooManyClients() ;
                doProcess() ;
            }
            if (isTraceOn()) {
                trace("run","Stop has been requested") ;
            }

        } catch(InterruptedException x) {
            if (isTraceOn()) {
                trace("run","Interrupt caught") ;
            }
            changeState(STOPPING);
        } catch(Exception x) {
            if (isDebugOn()) {
                debug("run","Unexpected exception = "+x) ;
            }
            changeState(STOPPING);
  } finally {
      synchronized (stateLock) {
    interrupted = true;
    Thread.currentThread().interrupted();
      }

      // ----------------------
      // unBind
      // ----------------------
      try {
    doUnbind() ;
    waitClientTermination() ;
    changeState(OFFLINE);
    if (isTraceOn()) {
        trace("run","State is OFFLINE") ;
    }
      } catch(Exception x) {
    if (isDebugOn()) {
        debug("run","Unexpected exception = "+x) ;
    }
    changeState(OFFLINE);
      }
     
  }
    }

    /**
     */
    protected abstract void doError(Exception e) throws CommunicationException;

    //
    // To be defined by the subclass.
    //
    // Each method below is called by run() and must be subclassed.
    // If the method sends an exception (Communication or Interrupt), this
    // will end up the run() method and switch the connector offline.
    //
    // If it is a CommunicationException, run() will call
    //       Debug.printException().
    //
    // All these methods should propagate the InterruptedException to inform
    // run() that the connector must be switch OFFLINE.
    //
    //
    //
    // doBind() should do all what is needed before calling doReceive().
    // If doBind() throws an exception, doUnbind() is not to be called
    // and run() ends up.
    //

    /**
     */
    protected abstract void doBind()
  throws CommunicationException, InterruptedException ;

    /**
     * <CODE>doReceive()</CODE> should block until a client is available.
     * If this method throws an exception, <CODE>doProcess()</CODE> is not
     * called but <CODE>doUnbind()</CODE> is called then <CODE>run()</CODE>
     * stops.
     */
    protected abstract void doReceive()
  throws CommunicationException, InterruptedException ;

    /**
     * <CODE>doProcess()</CODE> is called after <CODE>doReceive()</CODE>:
     * it should process the requests of the incoming client.
     * If it throws an exception, <CODE>doUnbind()</CODE> is called and
     * <CODE>run()</CODE> stops.
     */
    protected abstract void doProcess()
  throws CommunicationException, InterruptedException ;

    /**
     * <CODE>doUnbind()</CODE> is called whenever the connector goes
     * <CODE>OFFLINE</CODE>, except if <CODE>doBind()</CODE> has thrown an
     * exception.
     */
    protected abstract void doUnbind()
  throws CommunicationException, InterruptedException ;

    /**
     * Get the <code>MBeanServer</code> object to which incoming requests are
     * sent.  This is either the MBean server in which this connector is
     * registered, or an <code>MBeanServerForwarder</code> leading to that
     * server.
     */
    public synchronized MBeanServer getMBeanServer() {
        return topMBS;
    }

    /**
     * Set the <code>MBeanServer</code> object to which incoming
     * requests are sent.  This must be either the MBean server in
     * which this connector is registered, or an
     * <code>MBeanServerForwarder</code> leading to that server.  An
     * <code>MBeanServerForwarder</code> <code>mbsf</code> leads to an
     * MBean server <code>mbs</code> if
     * <code>mbsf.getMBeanServer()</code> is either <code>mbs</code>
     * or an <code>MBeanServerForwarder</code> leading to
     * <code>mbs</code>.
     *
     * @exception IllegalArgumentException if <code>newMBS</code> is neither
     * the MBean server in which this connector is registered nor an
     * <code>MBeanServerForwarder</code> leading to that server.
     *
     * @exception IllegalStateException This method has been invoked
     * while the communicator was ONLINE or STARTING.
     */
    public synchronized void setMBeanServer(MBeanServer newMBS)
      throws IllegalArgumentException, IllegalStateException {
  synchronized (stateLock) {
      if (state == ONLINE || state == STARTING)
    throw new IllegalStateException("Stop server before " +
            "carrying out this operation");
  }
  final String error =
      "MBeanServer argument must be MBean server where this " +
      "server is registered, or an MBeanServerForwarder " +
      "leading to that server";
  Vector seenMBS = new Vector();
  for (MBeanServer mbs = newMBS;
       mbs != bottomMBS;
       mbs = ((MBeanServerForwarder) mbs).getMBeanServer()) {
      if (!(mbs instanceof MBeanServerForwarder))
    throw new IllegalArgumentException(error);
      if (seenMBS.contains(mbs))
    throw new IllegalArgumentException("MBeanServerForwarder " +
               "loop");
      seenMBS.addElement(mbs);
  }
  topMBS = newMBS;
    }

    //
    // To be called by the subclass if needed
    //
    /**
     * For internal use only.
     */
    ObjectName getObjectName() {
        return objectName ;
    }

    /**
     * For internal use only.
     */
    void changeState(int newState) {
  int oldState;
  synchronized (stateLock) {
      if (state == newState)
    return;
      oldState = state;
      state = newState;
      stateLock.notifyAll();
  }
  sendStateChangeNotification(oldState, newState);
    }
   
    /**
     * Returns the string used in debug traces.
     */
    String makeDebugTag() {
        return "CommunicatorServer["+ getProtocol() + ":" + getPort() + "]" ;
    }

    /**
     * Returns the string used to name the connector thread.
     */
    String makeThreadName() {
        String result ;

        if (objectName == null)
            result = "CommunicatorServer" ;
        else
            result = objectName.toString() ;
       
        return result ;
    }
 
    /**
     * This method blocks if there are too many active clients.
     * Call to <CODE>wait()</CODE> is terminated when a client handler
     * thread calls <CODE>notifyClientHandlerDeleted(this)</CODE> ;
     */
    private synchronized void waitIfTooManyClients()
  throws InterruptedException {
        while (getActiveClientCount() >= maxActiveClientCount) {
            if (isTraceOn()) {
                trace("waitIfTooManyClients",
          "Waiting for a client to terminate") ;
            }
            wait();
        }
    }

    /**
     * This method blocks until there is no more active client.
     */
    private void waitClientTermination() {
        int s = clientHandlerVector.size() ;
        if (isTraceOn()) {       
            if (s >= 1) {
                trace("waitClientTermination","waiting for " +
                      s + " clients to terminate") ;
            }
        }

        // The ClientHandler will remove themselves from the
        // clientHandlerVector at the end of their run() method, by
        // calling notifyClientHandlerDeleted().
        // Since the clientHandlerVector is modified by the ClientHandler
        // threads we must avoid using Enumeration or Iterator to loop
        // over this array. We must also take care of NoSuchElementException
        // which could be thrown if the last ClientHandler removes itself
        // between the call to clientHandlerVector.isEmpty() and the call
        // to clientHandlerVector.firstElement().
        // What we *MUST NOT DO* is locking the clientHandlerVector, because
        // this would most probably cause a deadlock.
        //
        while (! clientHandlerVector.isEmpty()) {
            try {
                clientHandlerVector.firstElement().join();
            } catch (NoSuchElementException x) {
                trace("waitClientTermination","No element left: " + x);
            }
        }

        if (isTraceOn()) { 
            if (s >= 1) {
                trace("waitClientTermination","Ok, let's go...") ;
            }
        }
    }
 
    /**
     * Call <CODE>interrupt()</CODE> on each pending client.
     */
    private void terminateAllClient() {
        final int s = clientHandlerVector.size() ;
        if (isTraceOn()) {
            if (s >= 1) {
                trace("terminateAllClient","Interrupting " + s + " clients") ;
            }
        }
   
        // The ClientHandler will remove themselves from the
        // clientHandlerVector at the end of their run() method, by
        // calling notifyClientHandlerDeleted().
        // Since the clientHandlerVector is modified by the ClientHandler
        // threads we must avoid using Enumeration or Iterator to loop
        // over this array.
        // We cannot use the same logic here than in waitClientTermination()
        // because there is no guarantee that calling interrupt() on the
        // ClientHandler will actually terminate the ClientHandler.
        // Since we do not want to wait for the actual ClientHandler
        // termination, we cannot simply loop over the array until it is
        // empty (this might result in calling interrupt() endlessly on
        // the same client handler. So what we do is simply take a snapshot
        // copy of the vector and loop over the copy.
        // What we *MUST NOT DO* is locking the clientHandlerVector, because
        // this would most probably cause a deadlock.
        //
        final  ClientHandler[] handlers =
                clientHandlerVector.toArray(new ClientHandler[0]);
         for (ClientHandler h : handlers) {
             try {
                 h.interrupt() ;
             } catch (Exception x) {
                 if (isTraceOn())
                    trace("terminateAllClient",
                          "Failed to interrupt pending request: "+x+
                          " - skiping");
            }
        }
    }

    /**
     * Controls the way the CommunicatorServer service is deserialized.
     */
    private void readObject(ObjectInputStream stream)
        throws IOException, ClassNotFoundException {
     
        // Call the default deserialization of the object.
        //
        stream.defaultReadObject();
     
        // Call the specific initialization for the CommunicatorServer service.
        // This is for transient structures to be initialized to specific
  // default values.
        //
  stateLock = new Object();
  state = OFFLINE;
        stopRequested = false;
        servedClientCount = 0;
        clientHandlerVector = new Vector<ClientHandler>();
  fatherThread = Thread.currentThread();
  mainThread = null;
  notifCount = 0;
  notifInfos = null;
  notifBroadcaster = new NotificationBroadcasterSupport();
  dbgTag = makeDebugTag();
    }
 

    //
    // NotificationBroadcaster
    //

    /**
     * Adds a listener for the notifications emitted by this
     * CommunicatorServer.
     * There is only one type of notifications sent by the CommunicatorServer:
     * they are <tt>{@link javax.management.AttributeChangeNotification}</tt>,
     * sent when the <tt>State</tt> attribute of this CommunicatorServer
     * changes.
     *
     * @param listener The listener object which will handle the emitted
     *        notifications.
     * @param filter The filter object. If filter is null, no filtering
     *        will be performed before handling notifications.
     * @param handback An object which will be sent back unchanged to the
     *        listener when a notification is emitted.
     *
     * @exception IllegalArgumentException Listener parameter is null.
     */
    public void addNotificationListener(NotificationListener listener,
          NotificationFilter filter,
          Object handback)
        throws java.lang.IllegalArgumentException {

  if (isDebugOn()) {
      debug("addNotificationListener","Adding listener "+ listener +
      " with filter "+ filter + " and handback "+ handback);
  }
  notifBroadcaster.addNotificationListener(listener, filter, handback);
    }
   
    /**
     * Removes the specified listener from this CommunicatorServer.
     * Note that if the listener has been registered with different
     * handback objects or notification filters, all entries corresponding
     * to the listener will be removed.
     *
     * @param listener The listener object to be removed.
     *
     * @exception ListenerNotFoundException The listener is not registered.
     */
    public void removeNotificationListener(NotificationListener listener)
        throws ListenerNotFoundException {

  if (isDebugOn()) {
      debug("removeNotificationListener","Removing listener "+ listener);
  }
  notifBroadcaster.removeNotificationListener(listener);
    }
   
    /**
     * Returns an array of MBeanNotificationInfo objects describing
     * the notification types sent by this CommunicatorServer.
     * There is only one type of notifications sent by the CommunicatorServer:
     * it is <tt>{@link javax.management.AttributeChangeNotification}</tt>
     * sent when the <tt>State</tt> attribute of this CommunicatorServer
     * changes.
     */
    public MBeanNotificationInfo[] getNotificationInfo() {
 
  // Initialize notifInfos on first call to getNotificationInfo()
  //
  if (notifInfos == null) {
      notifInfos = new MBeanNotificationInfo[1];
      String[] notifTypes = {
    AttributeChangeNotification.ATTRIBUTE_CHANGE};
      notifInfos[0] = new MBeanNotificationInfo( notifTypes,
         AttributeChangeNotification.class.getName(),
         "Sent to notify that the value of the State attribute "+
         "of this CommunicatorServer instance has changed.");
  }

  return notifInfos;
    }
   
    /**
     *
     */
    private void sendStateChangeNotification(int oldState, int newState) {

  String oldStateString = getStringForState(oldState);
  String newStateString = getStringForState(newState);
  String message = new StringBuffer().append(dbgTag)
      .append(" The value of attribute State has changed from ")
      .append(oldState).append(" (").append(oldStateString)
      .append(") to ").append(newState).append(" (")
      .append(newStateString).append(").").toString();

  notifCount++;
  AttributeChangeNotification notif =
      new AttributeChangeNotification(this,    // source
             notifCount,               // sequence number
       System.currentTimeMillis(), // time stamp
       message,         // message
       "State",         // attribute name
       "int",           // attribute type
       new Integer(oldState),      // old value
       new Integer(newState) );    // new value
 
  if (isDebugOn()) {
      debug("sendStateChangeNotification",
      "Sending AttributeChangeNotification #"+ notifCount +
      " with message: "+ message);     
  }
  notifBroadcaster.sendNotification(notif);
    }

    /**
     *
     */
    private static String getStringForState(int s) {
        switch (s) {
        case ONLINE:   return "ONLINE";
        case STARTING: return "STARTING";
        case OFFLINE:  return "OFFLINE";
        case STOPPING: return "STOPPING";
  default:       return "UNDEFINED";
        }
    }


    //
    // MBeanRegistration
    //

    /**
     * Preregister method of connector.
     *
     *@param server The <CODE>MBeanServer</CODE> in which the MBean will
     *       be registered.
     *@param name The object name of the MBean.
     *
     *@return  The name of the MBean registered.
     *
     *@exception java.langException This exception should be caught by
     *           the <CODE>MBeanServer</CODE> and re-thrown
     *           as an <CODE>MBeanRegistrationException</CODE>.
     */
    public ObjectName preRegister(MBeanServer server, ObjectName name)
      throws java.lang.Exception {
        objectName = name;
  synchronized (this) {
      if (bottomMBS != null) {
    throw new IllegalArgumentException("connector already " +
               "registered in an MBean " +
               "server");
      }
      topMBS = bottomMBS = server;
  }
        dbgTag = makeDebugTag();
        return name;
    }

    /**
     *
     *@param registrationDone Indicates whether or not the MBean has been
     *       successfully registered in the <CODE>MBeanServer</CODE>.
     *       The value false means that the registration phase has failed.
     */
    public void postRegister(Boolean registrationDone) {
  if (!registrationDone.booleanValue()) {
      synchronized (this) {
    topMBS = bottomMBS = null;
      }
  }
    }
   
    /**
     * Stop the connector.
     *
     * @exception java.langException This exception should be caught by
     *            the <CODE>MBeanServer</CODE> and re-thrown
     *            as an <CODE>MBeanRegistrationException</CODE>.
     */
    public void preDeregister() throws java.lang.Exception {
  synchronized (this) {
      topMBS = bottomMBS = null;
  }
        objectName = null ;
  final int cstate = getState();
        if ((cstate == ONLINE) || ( cstate == STARTING)) {
            stop() ;
        }
    }

    /**
     * Do nothing.
     */
    public void postDeregister(){
    }

    /**
     * Load a class using the default loader repository
     **/
    Class loadClass(String className)
  throws ClassNotFoundException {
  try {
      return Class.forName(className);
  } catch (ClassNotFoundException e) {
      final ClassLoaderRepository clr =
    MBeanServerFactory.getClassLoaderRepository(bottomMBS);
      if (clr == null) throw new ClassNotFoundException(className);
      return clr.loadClass(className);
  }
    }

    //
    // Debug stuff
    //

    /**
     */
    int infoType;

    /**
     */
    boolean isTraceOn() {
        return Trace.isSelected(Trace.LEVEL_TRACE, infoType);
    }

    /**
     */
    void trace(String clz, String func, String info) {
        Trace.send(Trace.LEVEL_TRACE, infoType, clz, func, info);
    }

    /**
     */
    boolean isDebugOn() {
        return Trace.isSelected(Trace.LEVEL_DEBUG, infoType);
    }

    /**
     */
    void debug(String clz, String func, String info) {
        Trace.send(Trace.LEVEL_DEBUG, infoType, clz, func, info);
    }

    /**
     */
    void debug(String clz, String func, Throwable exception) {
        Trace.send(Trace.LEVEL_DEBUG, infoType, clz, func, exception);
    }
   
    /**
     */
    void trace(String func, String info) {
        trace(dbgTag, func, info);
    }

    /**
     */
    void debug(String func, String info) {
        debug(dbgTag, func, info);
    }

    /**
     */
    void debug(String func, Throwable exception) {
        debug(dbgTag, func, exception);
    }
}
TOP

Related Classes of com.sun.jmx.snmp.daemon.CommunicatorServer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.