1 /*
   2  * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package com.sun.jmx.snmp.daemon;
  28 
  29 
  30 
  31 // java import
  32 //
  33 import java.io.ObjectInputStream;
  34 import java.io.IOException;
  35 import java.net.InetAddress;
  36 import java.util.logging.Level;
  37 import java.util.Vector;
  38 import java.util.NoSuchElementException;
  39 
  40 // jmx import
  41 //
  42 import javax.management.MBeanServer;
  43 import javax.management.MBeanRegistration;
  44 import javax.management.ObjectName;
  45 import javax.management.NotificationListener;
  46 import javax.management.NotificationFilter;
  47 import javax.management.NotificationBroadcaster;
  48 import javax.management.NotificationBroadcasterSupport;
  49 import javax.management.MBeanNotificationInfo;
  50 import javax.management.AttributeChangeNotification;
  51 import javax.management.ListenerNotFoundException;
  52 
  53 import static com.sun.jmx.defaults.JmxProperties.SNMP_ADAPTOR_LOGGER;
  54 
  55 // JSR 160 import
  56 //
  57 // XXX Revisit:
  58 //   used to import com.sun.jmx.snmp.MBeanServerForwarder
  59 // Now using JSR 160 instead. => this is an additional
  60 // dependency to JSR 160.
  61 //
  62 import javax.management.remote.MBeanServerForwarder;
  63 
  64 /**
  65  * Defines generic behavior for the server part of a connector or an adaptor.
  66  * Most connectors or adaptors extend <CODE>CommunicatorServer</CODE>
  67  * and inherit this behavior. Connectors or adaptors that do not fit into
  68  * this model do not extend <CODE>CommunicatorServer</CODE>.
  69  * <p>
  70  * A <CODE>CommunicatorServer</CODE> is an active object, it listens for
  71  * client requests  and processes them in its own thread. When necessary, a
  72  * <CODE>CommunicatorServer</CODE> creates other threads to process multiple
  73  * requests concurrently.
  74  * <p>
  75  * A <CODE>CommunicatorServer</CODE> object can be stopped by calling the
  76  * <CODE>stop</CODE> method. When it is stopped, the
  77  * <CODE>CommunicatorServer</CODE> no longer listens to client requests and
  78  * no longer holds any thread or communication resources.
  79  * It can be started again by calling the <CODE>start</CODE> method.
  80  * <p>
  81  * A <CODE>CommunicatorServer</CODE> has a <CODE>State</CODE> attribute
  82  * which reflects its  activity.
  83  * <p>
  84  * <TABLE>
  85  * <TR><TH>CommunicatorServer</TH>      <TH>State</TH></TR>
  86  * <TR><TD><CODE>stopped</CODE></TD>    <TD><CODE>OFFLINE</CODE></TD></TR>
  87  * <TR><TD><CODE>starting</CODE></TD>    <TD><CODE>STARTING</CODE></TD></TR>
  88  * <TR><TD><CODE>running</CODE></TD>     <TD><CODE>ONLINE</CODE></TD></TR>
  89  * <TR><TD><CODE>stopping</CODE></TD>     <TD><CODE>STOPPING</CODE></TD></TR>
  90  * </TABLE>
  91  * <p>
  92  * The <CODE>STARTING</CODE> state marks the transition
  93  * from <CODE>OFFLINE</CODE> to <CODE>ONLINE</CODE>.
  94  * <p>
  95  * The <CODE>STOPPING</CODE> state marks the transition from
  96  * <CODE>ONLINE</CODE> to <CODE>OFFLINE</CODE>. This occurs when the
  97  * <CODE>CommunicatorServer</CODE> is finishing or interrupting active
  98  * requests.
  99  * <p>
 100  * When a <CODE>CommunicatorServer</CODE> is unregistered from the MBeanServer,
 101  * it is stopped automatically.
 102  * <p>
 103  * When the value of the <CODE>State</CODE> attribute changes the
 104  * <CODE>CommunicatorServer</CODE> sends a
 105  * <tt>{@link javax.management.AttributeChangeNotification}</tt> to the
 106  * registered listeners, if any.
 107  *
 108  * <p><b>This API is a Sun Microsystems internal API  and is subject
 109  * to change without notice.</b></p>
 110  */
 111 
 112 public abstract class CommunicatorServer
 113     implements Runnable, MBeanRegistration, NotificationBroadcaster,
 114                CommunicatorServerMBean {
 115 
 116     //
 117     // States of a CommunicatorServer
 118     //
 119 
 120     /**
 121      * Represents an <CODE>ONLINE</CODE> state.
 122      */
 123     public static final int ONLINE = 0 ;
 124 
 125     /**
 126      * Represents an <CODE>OFFLINE</CODE> state.
 127      */
 128     public static final int OFFLINE = 1 ;
 129 
 130     /**
 131      * Represents a <CODE>STOPPING</CODE> state.
 132      */
 133     public static final int STOPPING = 2 ;
 134 
 135     /**
 136      * Represents a <CODE>STARTING</CODE> state.
 137      */
 138     public static final int STARTING = 3 ;
 139 
 140     //
 141     // Types of connectors.
 142     //
 143 
 144     /**
 145      * Indicates that it is an RMI connector type.
 146      */
 147     //public static final int RMI_TYPE = 1 ;
 148 
 149     /**
 150      * Indicates that it is an HTTP connector type.
 151      */
 152     //public static final int HTTP_TYPE = 2 ;
 153 
 154     /**
 155      * Indicates that it is an HTML connector type.
 156      */
 157     //public static final int HTML_TYPE = 3 ;
 158 
 159     /**
 160      * Indicates that it is an SNMP connector type.
 161      */
 162     public static final int SNMP_TYPE = 4 ;
 163 
 164     /**
 165      * Indicates that it is an HTTPS connector type.
 166      */
 167     //public static final int HTTPS_TYPE = 5 ;
 168 
 169     //
 170     // Package variables
 171     //
 172 
 173     /**
 174      * The state of the connector server.
 175      */
 176      transient volatile int state = OFFLINE ;
 177 
 178     /**
 179      * The object name of the connector server.
 180      * @serial
 181      */
 182     ObjectName objectName ;
 183 
 184     MBeanServer topMBS;
 185     MBeanServer bottomMBS;
 186 
 187     /**
 188      */
 189     transient String dbgTag = null ;
 190 
 191     /**
 192      * The maximum number of clients that the CommunicatorServer can
 193      * process concurrently.
 194      * @serial
 195      */
 196     int maxActiveClientCount = 1 ;
 197 
 198     /**
 199      */
 200     transient int servedClientCount = 0 ;
 201 
 202     /**
 203      * The host name used by this CommunicatorServer.
 204      * @serial
 205      */
 206     String host = null ;
 207 
 208     /**
 209      * The port number used by this CommunicatorServer.
 210      * @serial
 211      */
 212     int port = -1 ;
 213 
 214 
 215     //
 216     // Private fields
 217     //
 218 
 219     /* This object controls access to the "state" and "interrupted" variables.
 220        If held at the same time as the lock on "this", the "this" lock must
 221        be taken first.  */
 222     private transient Object stateLock = new Object();
 223 
 224     private transient Vector<ClientHandler>
 225             clientHandlerVector = new Vector<>() ;
 226 
 227     private transient Thread mainThread = null ;
 228 
 229     private volatile boolean stopRequested = false ;
 230     private boolean interrupted = false;
 231     private transient Exception startException = null;
 232 
 233     // Notifs count, broadcaster and info
 234     private transient long notifCount = 0;
 235     private transient NotificationBroadcasterSupport notifBroadcaster =
 236         new NotificationBroadcasterSupport();
 237     private transient MBeanNotificationInfo[] notifInfos = null;
 238 
 239 
 240     /**
 241      * Instantiates a <CODE>CommunicatorServer</CODE>.
 242      *
 243      * @param connectorType Indicates the connector type. Possible values are:
 244      * SNMP_TYPE.
 245      *
 246      * @exception <CODE>java.lang.IllegalArgumentException</CODE>
 247      *            This connector type is not correct.
 248      */
 249     public CommunicatorServer(int connectorType)
 250         throws IllegalArgumentException {
 251         switch (connectorType) {
 252         case SNMP_TYPE :
 253             //No op. int Type deciding debugging removed.
 254             break;
 255         default:
 256             throw new IllegalArgumentException("Invalid connector Type") ;
 257         }
 258         dbgTag = makeDebugTag() ;
 259     }
 260 
 261     protected Thread createMainThread() {
 262         return new Thread (this, makeThreadName());
 263     }
 264 
 265     /**
 266      * Starts this <CODE>CommunicatorServer</CODE>.
 267      * <p>
 268      * Has no effect if this <CODE>CommunicatorServer</CODE> is
 269      * <CODE>ONLINE</CODE> or <CODE>STOPPING</CODE>.
 270      * @param timeout Time in ms to wait for the connector to start.
 271      *        If <code>timeout</code> is positive, wait for at most
 272      *        the specified time. An infinite timeout can be specified
 273      *        by passing a <code>timeout</code> value equals
 274      *        <code>Long.MAX_VALUE</code>. In that case the method
 275      *        will wait until the connector starts or fails to start.
 276      *        If timeout is negative or zero, returns as soon as possible
 277      *        without waiting.
 278      * @exception CommunicationException if the connectors fails to start.
 279      * @exception InterruptedException if the thread is interrupted or the
 280      *            timeout expires.
 281      */
 282     public void start(long timeout)
 283         throws CommunicationException, InterruptedException {
 284         boolean start;
 285 
 286         synchronized (stateLock) {
 287             if (state == STOPPING) {
 288                 // Fix for bug 4352451:
 289                 //     "java.net.BindException: Address in use".
 290                 waitState(OFFLINE, 60000);
 291             }
 292             start = (state == OFFLINE);
 293             if (start) {
 294                 changeState(STARTING);
 295                 stopRequested = false;
 296                 interrupted = false;
 297                 startException = null;
 298             }
 299         }
 300 
 301         if (!start) {
 302             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 303                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 304                     "start","Connector is not OFFLINE");
 305             }
 306             return;
 307         }
 308 
 309         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 310             SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 311                 "start","--> Start connector ");
 312         }
 313 
 314         mainThread = createMainThread();
 315 
 316         mainThread.start() ;
 317 
 318         if (timeout > 0) waitForStart(timeout);
 319     }
 320 
 321     /**
 322      * Starts this <CODE>CommunicatorServer</CODE>.
 323      * <p>
 324      * Has no effect if this <CODE>CommunicatorServer</CODE> is
 325      * <CODE>ONLINE</CODE> or <CODE>STOPPING</CODE>.
 326      */
 327     @Override
 328     public void start() {
 329         try {
 330             start(0);
 331         } catch (InterruptedException x) {
 332             // cannot happen because of `0'
 333             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 334                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 335                     "start","interrupted", x);
 336             }
 337         }
 338     }
 339 
 340     /**
 341      * Stops this <CODE>CommunicatorServer</CODE>.
 342      * <p>
 343      * Has no effect if this <CODE>CommunicatorServer</CODE> is
 344      * <CODE>OFFLINE</CODE> or  <CODE>STOPPING</CODE>.
 345      */
 346     @Override
 347     public void stop() {
 348         synchronized (stateLock) {
 349             if (state == OFFLINE || state == STOPPING) {
 350                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 351                     SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 352                         "stop","Connector is not ONLINE");
 353                 }
 354                 return;
 355             }
 356             changeState(STOPPING);
 357             //
 358             // Stop the connector thread
 359             //
 360             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 361                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 362                     "stop","Interrupt main thread");
 363             }
 364             stopRequested = true ;
 365             if (!interrupted) {
 366                 interrupted = true;
 367                 mainThread.interrupt();
 368             }
 369         }
 370 
 371         //
 372         // Call terminate on each active client handler
 373         //
 374         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 375             SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 376                 "stop","terminateAllClient");
 377         }
 378         terminateAllClient() ;
 379 
 380         // ----------------------
 381         // changeState
 382         // ----------------------
 383         synchronized (stateLock) {
 384             if (state == STARTING)
 385                 changeState(OFFLINE);
 386         }
 387     }
 388 
 389     /**
 390      * Tests whether the <CODE>CommunicatorServer</CODE> is active.
 391      *
 392      * @return True if connector is <CODE>ONLINE</CODE>; false otherwise.
 393      */
 394     @Override
 395     public boolean isActive() {
 396         synchronized (stateLock) {
 397             return (state == ONLINE);
 398         }
 399     }
 400 
 401     /**
 402      * <p>Waits until either the State attribute of this MBean equals the
 403      * specified <VAR>wantedState</VAR> parameter,
 404      * or the specified  <VAR>timeOut</VAR> has elapsed.
 405      * The method <CODE>waitState</CODE> returns with a boolean value
 406      * indicating whether the specified <VAR>wantedState</VAR> parameter
 407      * equals the value of this MBean's State attribute at the time the method
 408      * terminates.</p>
 409      *
 410      * <p>Two special cases for the <VAR>timeOut</VAR> parameter value are:</p>
 411      * <UL><LI> if <VAR>timeOut</VAR> is negative then <CODE>waitState</CODE>
 412      *     returns immediately (i.e. does not wait at all),</LI>
 413      * <LI> if <VAR>timeOut</VAR> equals zero then <CODE>waitState</CODE>
 414      *     waits untill the value of this MBean's State attribute
 415      *     is the same as the <VAR>wantedState</VAR> parameter (i.e. will wait
 416      *     indefinitely if this condition is never met).</LI></UL>
 417      *
 418      * @param wantedState The value of this MBean's State attribute to wait
 419      *        for. <VAR>wantedState</VAR> can be one of:
 420      * <ul>
 421      * <li><CODE>CommunicatorServer.OFFLINE</CODE>,</li>
 422      * <li><CODE>CommunicatorServer.ONLINE</CODE>,</li>
 423      * <li><CODE>CommunicatorServer.STARTING</CODE>,</li>
 424      * <li><CODE>CommunicatorServer.STOPPING</CODE>.</li>
 425      * </ul>
 426      * @param timeOut The maximum time to wait for, in milliseconds,
 427      *        if positive.
 428      * Infinite time out if 0, or no waiting at all if negative.
 429      *
 430      * @return true if the value of this MBean's State attribute is the
 431      *      same as the <VAR>wantedState</VAR> parameter; false otherwise.
 432      */
 433     @Override
 434     public boolean waitState(int wantedState, long timeOut) {
 435         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 436             SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 437                 "waitState", wantedState + "(0on,1off,2st) TO=" + timeOut +
 438                   " ; current state = " + getStateString());
 439         }
 440 
 441         long endTime = 0;
 442         if (timeOut > 0)
 443             endTime = System.currentTimeMillis() + timeOut;
 444 
 445         synchronized (stateLock) {
 446             while (state != wantedState) {
 447                 if (timeOut < 0) {
 448                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 449                         SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 450                             "waitState", "timeOut < 0, return without wait");
 451                     }
 452                     return false;
 453                 } else {
 454                     try {
 455                         if (timeOut > 0) {
 456                             long toWait = endTime - System.currentTimeMillis();
 457                             if (toWait <= 0) {
 458                                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 459                                     SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 460                                         "waitState", "timed out");
 461                                 }
 462                                 return false;
 463                             }
 464                             stateLock.wait(toWait);
 465                         } else {  // timeOut == 0
 466                             stateLock.wait();
 467                         }
 468                     } catch (InterruptedException e) {
 469                         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 470                             SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 471                                 "waitState", "wait interrupted");
 472                         }
 473                         return (state == wantedState);
 474                     }
 475                 }
 476             }
 477             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 478                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 479                     "waitState","returning in desired state");
 480             }
 481             return true;
 482         }
 483     }
 484 
 485     /**
 486      * <p>Waits until the communicator is started or timeout expires.
 487      *
 488      * @param timeout Time in ms to wait for the connector to start.
 489      *        If <code>timeout</code> is positive, wait for at most
 490      *        the specified time. An infinite timeout can be specified
 491      *        by passing a <code>timeout</code> value equals
 492      *        <code>Long.MAX_VALUE</code>. In that case the method
 493      *        will wait until the connector starts or fails to start.
 494      *        If timeout is negative or zero, returns as soon as possible
 495      *        without waiting.
 496      *
 497      * @exception CommunicationException if the connectors fails to start.
 498      * @exception InterruptedException if the thread is interrupted or the
 499      *            timeout expires.
 500      *
 501      */
 502     private void waitForStart(long timeout)
 503         throws CommunicationException, InterruptedException {
 504         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 505             SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 506                 "waitForStart", "Timeout=" + timeout +
 507                  " ; current state = " + getStateString());
 508         }
 509 
 510         final long startTime = System.currentTimeMillis();
 511 
 512         synchronized (stateLock) {
 513             while (state == STARTING) {
 514                 // Time elapsed since startTime...
 515                 //
 516                 final long elapsed = System.currentTimeMillis() - startTime;
 517 
 518                 // wait for timeout - elapsed.
 519                 // A timeout of Long.MAX_VALUE is equivalent to something
 520                 // like 292271023 years - which is pretty close to
 521                 // forever as far as we are concerned ;-)
 522                 //
 523                 final long remainingTime = timeout-elapsed;
 524 
 525                 // If remainingTime is negative, the timeout has elapsed.
 526                 //
 527                 if (remainingTime < 0) {
 528                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 529                         SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 530                             "waitForStart", "timeout < 0, return without wait");
 531                     }
 532                     throw new InterruptedException("Timeout expired");
 533                 }
 534 
 535                 // We're going to wait until someone notifies on the
 536                 // the stateLock object, or until the timeout expires,
 537                 // or until the thread is interrupted.
 538                 //
 539                 try {
 540                     stateLock.wait(remainingTime);
 541                 } catch (InterruptedException e) {
 542                     if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 543                         SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 544                             "waitForStart", "wait interrupted");
 545                     }
 546 
 547                     // If we are now ONLINE, then no need to rethrow the
 548                     // exception... we're simply going to exit the while
 549                     // loop. Otherwise, throw the InterruptedException.
 550                     //
 551                     if (state != ONLINE) throw e;
 552                 }
 553             }
 554 
 555             // We're no longer in STARTING state
 556             //
 557             if (state == ONLINE) {
 558                 // OK, we're started, everything went fine, just return
 559                 //
 560                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 561                     SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 562                         "waitForStart", "started");
 563                 }
 564                 return;
 565             } else if (startException instanceof CommunicationException) {
 566                 // There was some exception during the starting phase.
 567                 // Cast and throw...
 568                 //
 569                 throw (CommunicationException)startException;
 570             } else if (startException instanceof InterruptedException) {
 571                 // There was some exception during the starting phase.
 572                 // Cast and throw...
 573                 //
 574                 throw (InterruptedException)startException;
 575             } else if (startException != null) {
 576                 // There was some exception during the starting phase.
 577                 // Wrap and throw...
 578                 //
 579                 throw new CommunicationException(startException,
 580                                                  "Failed to start: "+
 581                                                  startException);
 582             } else {
 583                 // We're not ONLINE, and there's no exception...
 584                 // Something went wrong but we don't know what...
 585                 //
 586                 throw new CommunicationException("Failed to start: state is "+
 587                                                  getStringForState(state));
 588             }
 589         }
 590     }
 591 
 592     /**
 593      * Gets the state of this <CODE>CommunicatorServer</CODE> as an integer.
 594      *
 595      * @return <CODE>ONLINE</CODE>, <CODE>OFFLINE</CODE>,
 596      *         <CODE>STARTING</CODE> or <CODE>STOPPING</CODE>.
 597      */
 598     @Override
 599     public int getState() {
 600         synchronized (stateLock) {
 601             return state ;
 602         }
 603     }
 604 
 605     /**
 606      * Gets the state of this <CODE>CommunicatorServer</CODE> as a string.
 607      *
 608      * @return One of the strings "ONLINE", "OFFLINE", "STARTING" or
 609      *         "STOPPING".
 610      */
 611     @Override
 612     public String getStateString() {
 613         return getStringForState(state) ;
 614     }
 615 
 616     /**
 617      * Gets the host name used by this <CODE>CommunicatorServer</CODE>.
 618      *
 619      * @return The host name used by this <CODE>CommunicatorServer</CODE>.
 620      */
 621     @Override
 622     public String getHost() {
 623         try {
 624             host = InetAddress.getLocalHost().getHostName();
 625         } catch (Exception e) {
 626             host = "Unknown host";
 627         }
 628         return host ;
 629     }
 630 
 631     /**
 632      * Gets the port number used by this <CODE>CommunicatorServer</CODE>.
 633      *
 634      * @return The port number used by this <CODE>CommunicatorServer</CODE>.
 635      */
 636     @Override
 637     public int getPort() {
 638         synchronized (stateLock) {
 639             return port ;
 640         }
 641     }
 642 
 643     /**
 644      * Sets the port number used by this <CODE>CommunicatorServer</CODE>.
 645      *
 646      * @param port The port number used by this
 647      *             <CODE>CommunicatorServer</CODE>.
 648      *
 649      * @exception java.lang.IllegalStateException This method has been invoked
 650      * while the communicator was ONLINE or STARTING.
 651      */
 652     @Override
 653     public void setPort(int port) throws java.lang.IllegalStateException {
 654         synchronized (stateLock) {
 655             if ((state == ONLINE) || (state == STARTING))
 656                 throw new IllegalStateException("Stop server before " +
 657                                                 "carrying out this operation");
 658             this.port = port;
 659             dbgTag = makeDebugTag();
 660         }
 661     }
 662 
 663     /**
 664      * Gets the protocol being used by this <CODE>CommunicatorServer</CODE>.
 665      * @return The protocol as a string.
 666      */
 667     @Override
 668     public abstract String getProtocol();
 669 
 670     /**
 671      * Gets the number of clients that have been processed by this
 672      * <CODE>CommunicatorServer</CODE>  since its creation.
 673      *
 674      * @return The number of clients handled by this
 675      *         <CODE>CommunicatorServer</CODE>
 676      *         since its creation. This counter is not reset by the
 677      *         <CODE>stop</CODE> method.
 678      */
 679     int getServedClientCount() {
 680         return servedClientCount ;
 681     }
 682 
 683     /**
 684      * Gets the number of clients currently being processed by this
 685      * <CODE>CommunicatorServer</CODE>.
 686      *
 687      * @return The number of clients currently being processed by this
 688      *         <CODE>CommunicatorServer</CODE>.
 689      */
 690     int getActiveClientCount() {
 691         int result = clientHandlerVector.size() ;
 692         return result ;
 693     }
 694 
 695     /**
 696      * Gets the maximum number of clients that this
 697      * <CODE>CommunicatorServer</CODE> can  process concurrently.
 698      *
 699      * @return The maximum number of clients that this
 700      *         <CODE>CommunicatorServer</CODE> can
 701      *         process concurrently.
 702      */
 703     int getMaxActiveClientCount() {
 704         return maxActiveClientCount ;
 705     }
 706 
 707     /**
 708      * Sets the maximum number of clients this
 709      * <CODE>CommunicatorServer</CODE> can process concurrently.
 710      *
 711      * @param c The number of clients.
 712      *
 713      * @exception java.lang.IllegalStateException This method has been invoked
 714      * while the communicator was ONLINE or STARTING.
 715      */
 716     void setMaxActiveClientCount(int c)
 717         throws java.lang.IllegalStateException {
 718         synchronized (stateLock) {
 719             if ((state == ONLINE) || (state == STARTING)) {
 720                 throw new IllegalStateException(
 721                           "Stop server before carrying out this operation");
 722             }
 723             maxActiveClientCount = c ;
 724         }
 725     }
 726 
 727     /**
 728      * For SNMP Runtime internal use only.
 729      */
 730     void notifyClientHandlerCreated(ClientHandler h) {
 731         clientHandlerVector.addElement(h) ;
 732     }
 733 
 734     /**
 735      * For SNMP Runtime internal use only.
 736      */
 737     synchronized void notifyClientHandlerDeleted(ClientHandler h) {
 738         clientHandlerVector.removeElement(h);
 739         notifyAll();
 740     }
 741 
 742     /**
 743      * The number of times the communicator server will attempt
 744      * to bind before giving up.
 745      **/
 746     protected int getBindTries() {
 747         return 50;
 748     }
 749 
 750     /**
 751      * The delay, in ms, during which the communicator server will sleep before
 752      * attempting to bind again.
 753      **/
 754     protected long getBindSleepTime() {
 755         return 100;
 756     }
 757 
 758     /**
 759      * For SNMP Runtime internal use only.
 760      * <p>
 761      * The <CODE>run</CODE> method executed by this connector's main thread.
 762      */
 763     @Override
 764     public void run() {
 765 
 766         // Fix jaw.00667.B
 767         // It seems that the init of "i" and "success"
 768         // need to be done outside the "try" clause...
 769         // A bug in Java 2 production release ?
 770         //
 771         int i = 0;
 772         boolean success = false;
 773 
 774         // ----------------------
 775         // Bind
 776         // ----------------------
 777         try {
 778             // Fix for bug 4352451: "java.net.BindException: Address in use".
 779             //
 780             final int  bindRetries = getBindTries();
 781             final long sleepTime   = getBindSleepTime();
 782             while (i < bindRetries && !success) {
 783                 try {
 784                     // Try socket connection.
 785                     //
 786                     doBind();
 787                     success = true;
 788                 } catch (CommunicationException ce) {
 789                     i++;
 790                     try {
 791                         Thread.sleep(sleepTime);
 792                     } catch (InterruptedException ie) {
 793                         throw ie;
 794                     }
 795                 }
 796             }
 797             // Retry last time to get correct exception.
 798             //
 799             if (!success) {
 800                 // Try socket connection.
 801                 //
 802                 doBind();
 803             }
 804 
 805         } catch(Exception x) {
 806             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 807                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 808                     "run", "Got unexpected exception", x);
 809             }
 810             synchronized(stateLock) {
 811                 startException = x;
 812                 changeState(OFFLINE);
 813             }
 814             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 815                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 816                     "run","State is OFFLINE");
 817             }
 818             doError(x);
 819             return;
 820         }
 821 
 822         try {
 823             // ----------------------
 824             // State change
 825             // ----------------------
 826             changeState(ONLINE) ;
 827             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 828                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 829                     "run","State is ONLINE");
 830             }
 831 
 832             // ----------------------
 833             // Main loop
 834             // ----------------------
 835             while (!stopRequested) {
 836                 servedClientCount++;
 837                 doReceive() ;
 838                 waitIfTooManyClients() ;
 839                 doProcess() ;
 840             }
 841             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 842                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 843                     "run","Stop has been requested");
 844             }
 845 
 846         } catch(InterruptedException x) {
 847             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 848                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 849                     "run","Interrupt caught");
 850             }
 851             changeState(STOPPING);
 852         } catch(Exception x) {
 853             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 854                 SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 855                     "run","Got unexpected exception", x);
 856             }
 857             changeState(STOPPING);
 858         } finally {
 859             synchronized (stateLock) {
 860                 interrupted = true;
 861                 Thread.interrupted();
 862             }
 863 
 864             // ----------------------
 865             // unBind
 866             // ----------------------
 867             try {
 868                 doUnbind() ;
 869                 waitClientTermination() ;
 870                 changeState(OFFLINE);
 871                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
 872                     SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
 873                         "run","State is OFFLINE");
 874                 }
 875             } catch(Exception x) {
 876                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
 877                     SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
 878                         "run","Got unexpected exception", x);
 879                 }
 880                 changeState(OFFLINE);
 881             }
 882 
 883         }
 884     }
 885 
 886     /**
 887      */
 888     protected abstract void doError(Exception e) throws CommunicationException;
 889 
 890     //
 891     // To be defined by the subclass.
 892     //
 893     // Each method below is called by run() and must be subclassed.
 894     // If the method sends an exception (Communication or Interrupt), this
 895     // will end up the run() method and switch the connector offline.
 896     //
 897     // If it is a CommunicationException, run() will call
 898     //       Debug.printException().
 899     //
 900     // All these methods should propagate the InterruptedException to inform
 901     // run() that the connector must be switch OFFLINE.
 902     //
 903     //
 904     //
 905     // doBind() should do all what is needed before calling doReceive().
 906     // If doBind() throws an exception, doUnbind() is not to be called
 907     // and run() ends up.
 908     //
 909 
 910     /**
 911      */
 912     protected abstract void doBind()
 913         throws CommunicationException, InterruptedException ;
 914 
 915     /**
 916      * <CODE>doReceive()</CODE> should block until a client is available.
 917      * If this method throws an exception, <CODE>doProcess()</CODE> is not
 918      * called but <CODE>doUnbind()</CODE> is called then <CODE>run()</CODE>
 919      * stops.
 920      */
 921     protected abstract void doReceive()
 922         throws CommunicationException, InterruptedException ;
 923 
 924     /**
 925      * <CODE>doProcess()</CODE> is called after <CODE>doReceive()</CODE>:
 926      * it should process the requests of the incoming client.
 927      * If it throws an exception, <CODE>doUnbind()</CODE> is called and
 928      * <CODE>run()</CODE> stops.
 929      */
 930     protected abstract void doProcess()
 931         throws CommunicationException, InterruptedException ;
 932 
 933     /**
 934      * <CODE>doUnbind()</CODE> is called whenever the connector goes
 935      * <CODE>OFFLINE</CODE>, except if <CODE>doBind()</CODE> has thrown an
 936      * exception.
 937      */
 938     protected abstract void doUnbind()
 939         throws CommunicationException, InterruptedException ;
 940 
 941     /**
 942      * Get the <code>MBeanServer</code> object to which incoming requests are
 943      * sent.  This is either the MBean server in which this connector is
 944      * registered, or an <code>MBeanServerForwarder</code> leading to that
 945      * server.
 946      */
 947     public synchronized MBeanServer getMBeanServer() {
 948         return topMBS;
 949     }
 950 
 951     /**
 952      * Set the <code>MBeanServer</code> object to which incoming
 953      * requests are sent.  This must be either the MBean server in
 954      * which this connector is registered, or an
 955      * <code>MBeanServerForwarder</code> leading to that server.  An
 956      * <code>MBeanServerForwarder</code> <code>mbsf</code> leads to an
 957      * MBean server <code>mbs</code> if
 958      * <code>mbsf.getMBeanServer()</code> is either <code>mbs</code>
 959      * or an <code>MBeanServerForwarder</code> leading to
 960      * <code>mbs</code>.
 961      *
 962      * @exception IllegalArgumentException if <code>newMBS</code> is neither
 963      * the MBean server in which this connector is registered nor an
 964      * <code>MBeanServerForwarder</code> leading to that server.
 965      *
 966      * @exception IllegalStateException This method has been invoked
 967      * while the communicator was ONLINE or STARTING.
 968      */
 969     public synchronized void setMBeanServer(MBeanServer newMBS)
 970             throws IllegalArgumentException, IllegalStateException {
 971         synchronized (stateLock) {
 972             if (state == ONLINE || state == STARTING)
 973                 throw new IllegalStateException("Stop server before " +
 974                                                 "carrying out this operation");
 975         }
 976         final String error =
 977             "MBeanServer argument must be MBean server where this " +
 978             "server is registered, or an MBeanServerForwarder " +
 979             "leading to that server";
 980         Vector<MBeanServer> seenMBS = new Vector<>();
 981         for (MBeanServer mbs = newMBS;
 982              mbs != bottomMBS;
 983              mbs = ((MBeanServerForwarder) mbs).getMBeanServer()) {
 984             if (!(mbs instanceof MBeanServerForwarder))
 985                 throw new IllegalArgumentException(error);
 986             if (seenMBS.contains(mbs))
 987                 throw new IllegalArgumentException("MBeanServerForwarder " +
 988                                                    "loop");
 989             seenMBS.addElement(mbs);
 990         }
 991         topMBS = newMBS;
 992     }
 993 
 994     //
 995     // To be called by the subclass if needed
 996     //
 997     /**
 998      * For internal use only.
 999      */
1000     ObjectName getObjectName() {
1001         return objectName ;
1002     }
1003 
1004     /**
1005      * For internal use only.
1006      */
1007     void changeState(int newState) {
1008         int oldState;
1009         synchronized (stateLock) {
1010             if (state == newState)
1011                 return;
1012             oldState = state;
1013             state = newState;
1014             stateLock.notifyAll();
1015         }
1016         sendStateChangeNotification(oldState, newState);
1017     }
1018 
1019     /**
1020      * Returns the string used in debug traces.
1021      */
1022     String makeDebugTag() {
1023         return "CommunicatorServer["+ getProtocol() + ":" + getPort() + "]" ;
1024     }
1025 
1026     /**
1027      * Returns the string used to name the connector thread.
1028      */
1029     String makeThreadName() {
1030         String result ;
1031 
1032         if (objectName == null)
1033             result = "CommunicatorServer" ;
1034         else
1035             result = objectName.toString() ;
1036 
1037         return result ;
1038     }
1039 
1040     /**
1041      * This method blocks if there are too many active clients.
1042      * Call to <CODE>wait()</CODE> is terminated when a client handler
1043      * thread calls <CODE>notifyClientHandlerDeleted(this)</CODE> ;
1044      */
1045     private synchronized void waitIfTooManyClients()
1046         throws InterruptedException {
1047         while (getActiveClientCount() >= maxActiveClientCount) {
1048             if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
1049                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
1050                     "waitIfTooManyClients","Waiting for a client to terminate");
1051             }
1052             wait();
1053         }
1054     }
1055 
1056     /**
1057      * This method blocks until there is no more active client.
1058      */
1059     private void waitClientTermination() {
1060         int s = clientHandlerVector.size() ;
1061         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
1062             if (s >= 1) {
1063                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
1064                 "waitClientTermination","waiting for " +
1065                       s + " clients to terminate");
1066             }
1067         }
1068 
1069         // The ClientHandler will remove themselves from the
1070         // clientHandlerVector at the end of their run() method, by
1071         // calling notifyClientHandlerDeleted().
1072         // Since the clientHandlerVector is modified by the ClientHandler
1073         // threads we must avoid using Enumeration or Iterator to loop
1074         // over this array. We must also take care of NoSuchElementException
1075         // which could be thrown if the last ClientHandler removes itself
1076         // between the call to clientHandlerVector.isEmpty() and the call
1077         // to clientHandlerVector.firstElement().
1078         // What we *MUST NOT DO* is locking the clientHandlerVector, because
1079         // this would most probably cause a deadlock.
1080         //
1081         while (! clientHandlerVector.isEmpty()) {
1082             try {
1083                 clientHandlerVector.firstElement().join();
1084             } catch (NoSuchElementException x) {
1085                 if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
1086                     SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
1087                         "waitClientTermination","No elements left",  x);
1088                 }
1089             }
1090         }
1091 
1092         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
1093             if (s >= 1) {
1094                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
1095                     "waitClientTermination","Ok, let's go...");
1096             }
1097         }
1098     }
1099 
1100     /**
1101      * Call <CODE>interrupt()</CODE> on each pending client.
1102      */
1103     private void terminateAllClient() {
1104         final int s = clientHandlerVector.size() ;
1105         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
1106             if (s >= 1) {
1107                 SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
1108                     "terminateAllClient","Interrupting " + s + " clients");
1109             }
1110         }
1111 
1112         // The ClientHandler will remove themselves from the
1113         // clientHandlerVector at the end of their run() method, by
1114         // calling notifyClientHandlerDeleted().
1115         // Since the clientHandlerVector is modified by the ClientHandler
1116         // threads we must avoid using Enumeration or Iterator to loop
1117         // over this array.
1118         // We cannot use the same logic here than in waitClientTermination()
1119         // because there is no guarantee that calling interrupt() on the
1120         // ClientHandler will actually terminate the ClientHandler.
1121         // Since we do not want to wait for the actual ClientHandler
1122         // termination, we cannot simply loop over the array until it is
1123         // empty (this might result in calling interrupt() endlessly on
1124         // the same client handler. So what we do is simply take a snapshot
1125         // copy of the vector and loop over the copy.
1126         // What we *MUST NOT DO* is locking the clientHandlerVector, because
1127         // this would most probably cause a deadlock.
1128         //
1129         final  ClientHandler[] handlers =
1130                 clientHandlerVector.toArray(new ClientHandler[0]);
1131          for (ClientHandler h : handlers) {
1132              try {
1133                  h.interrupt() ;
1134              } catch (Exception x) {
1135                  if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
1136                      SNMP_ADAPTOR_LOGGER.logp(Level.FINER, dbgTag,
1137                              "terminateAllClient",
1138                              "Failed to interrupt pending request. " +
1139                              "Ignore the exception.", x);
1140                  }
1141             }
1142         }
1143     }
1144 
1145     /**
1146      * Controls the way the CommunicatorServer service is deserialized.
1147      */
1148     private void readObject(ObjectInputStream stream)
1149         throws IOException, ClassNotFoundException {
1150 
1151         // Call the default deserialization of the object.
1152         //
1153         stream.defaultReadObject();
1154 
1155         // Call the specific initialization for the CommunicatorServer service.
1156         // This is for transient structures to be initialized to specific
1157         // default values.
1158         //
1159         stateLock = new Object();
1160         state = OFFLINE;
1161         stopRequested = false;
1162         servedClientCount = 0;
1163         clientHandlerVector = new Vector<>();
1164         mainThread = null;
1165         notifCount = 0;
1166         notifInfos = null;
1167         notifBroadcaster = new NotificationBroadcasterSupport();
1168         dbgTag = makeDebugTag();
1169     }
1170 
1171 
1172     //
1173     // NotificationBroadcaster
1174     //
1175 
1176     /**
1177      * Adds a listener for the notifications emitted by this
1178      * CommunicatorServer.
1179      * There is only one type of notifications sent by the CommunicatorServer:
1180      * they are <tt>{@link javax.management.AttributeChangeNotification}</tt>,
1181      * sent when the <tt>State</tt> attribute of this CommunicatorServer
1182      * changes.
1183      *
1184      * @param listener The listener object which will handle the emitted
1185      *        notifications.
1186      * @param filter The filter object. If filter is null, no filtering
1187      *        will be performed before handling notifications.
1188      * @param handback An object which will be sent back unchanged to the
1189      *        listener when a notification is emitted.
1190      *
1191      * @exception IllegalArgumentException Listener parameter is null.
1192      */
1193     @Override
1194     public void addNotificationListener(NotificationListener listener,
1195                                         NotificationFilter filter,
1196                                         Object handback)
1197         throws java.lang.IllegalArgumentException {
1198 
1199         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
1200             SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
1201                 "addNotificationListener","Adding listener "+ listener +
1202                   " with filter "+ filter + " and handback "+ handback);
1203         }
1204         notifBroadcaster.addNotificationListener(listener, filter, handback);
1205     }
1206 
1207     /**
1208      * Removes the specified listener from this CommunicatorServer.
1209      * Note that if the listener has been registered with different
1210      * handback objects or notification filters, all entries corresponding
1211      * to the listener will be removed.
1212      *
1213      * @param listener The listener object to be removed.
1214      *
1215      * @exception ListenerNotFoundException The listener is not registered.
1216      */
1217     @Override
1218     public void removeNotificationListener(NotificationListener listener)
1219         throws ListenerNotFoundException {
1220 
1221         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
1222             SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
1223                 "removeNotificationListener","Removing listener "+ listener);
1224         }
1225         notifBroadcaster.removeNotificationListener(listener);
1226     }
1227 
1228     /**
1229      * Returns an array of MBeanNotificationInfo objects describing
1230      * the notification types sent by this CommunicatorServer.
1231      * There is only one type of notifications sent by the CommunicatorServer:
1232      * it is <tt>{@link javax.management.AttributeChangeNotification}</tt>,
1233      * sent when the <tt>State</tt> attribute of this CommunicatorServer
1234      * changes.
1235      */
1236     @Override
1237     public MBeanNotificationInfo[] getNotificationInfo() {
1238 
1239         // Initialize notifInfos on first call to getNotificationInfo()
1240         //
1241         if (notifInfos == null) {
1242             notifInfos = new MBeanNotificationInfo[1];
1243             String[] notifTypes = {
1244                 AttributeChangeNotification.ATTRIBUTE_CHANGE};
1245             notifInfos[0] = new MBeanNotificationInfo( notifTypes,
1246                      AttributeChangeNotification.class.getName(),
1247                      "Sent to notify that the value of the State attribute "+
1248                      "of this CommunicatorServer instance has changed.");
1249         }
1250 
1251         return notifInfos.clone();
1252     }
1253 
1254     /**
1255      *
1256      */
1257     private void sendStateChangeNotification(int oldState, int newState) {
1258 
1259         String oldStateString = getStringForState(oldState);
1260         String newStateString = getStringForState(newState);
1261         String message = new StringBuffer().append(dbgTag)
1262             .append(" The value of attribute State has changed from ")
1263             .append(oldState).append(" (").append(oldStateString)
1264             .append(") to ").append(newState).append(" (")
1265             .append(newStateString).append(").").toString();
1266 
1267         notifCount++;
1268         AttributeChangeNotification notif =
1269             new AttributeChangeNotification(this,    // source
1270                          notifCount,                 // sequence number
1271                          System.currentTimeMillis(), // time stamp
1272                          message,                    // message
1273                          "State",                    // attribute name
1274                          "int",                      // attribute type
1275                          oldState,                   // old value
1276                          newState );                 // new value
1277         if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
1278             SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, dbgTag,
1279                 "sendStateChangeNotification","Sending AttributeChangeNotification #"
1280                     + notifCount + " with message: "+ message);
1281         }
1282         notifBroadcaster.sendNotification(notif);
1283     }
1284 
1285     /**
1286      *
1287      */
1288     private static String getStringForState(int s) {
1289         switch (s) {
1290         case ONLINE:   return "ONLINE";
1291         case STARTING: return "STARTING";
1292         case OFFLINE:  return "OFFLINE";
1293         case STOPPING: return "STOPPING";
1294         default:       return "UNDEFINED";
1295         }
1296     }
1297 
1298 
1299     //
1300     // MBeanRegistration
1301     //
1302 
1303     /**
1304      * Preregister method of connector.
1305      *
1306      *@param server The <CODE>MBeanServer</CODE> in which the MBean will
1307      *       be registered.
1308      *@param name The object name of the MBean.
1309      *
1310      *@return  The name of the MBean registered.
1311      *
1312      *@exception java.langException This exception should be caught by
1313      *           the <CODE>MBeanServer</CODE> and re-thrown
1314      *           as an <CODE>MBeanRegistrationException</CODE>.
1315      */
1316     @Override
1317     public ObjectName preRegister(MBeanServer server, ObjectName name)
1318             throws java.lang.Exception {
1319         objectName = name;
1320         synchronized (this) {
1321             if (bottomMBS != null) {
1322                 throw new IllegalArgumentException("connector already " +
1323                                                    "registered in an MBean " +
1324                                                    "server");
1325             }
1326             topMBS = bottomMBS = server;
1327         }
1328         dbgTag = makeDebugTag();
1329         return name;
1330     }
1331 
1332     /**
1333      *
1334      *@param registrationDone Indicates whether or not the MBean has been
1335      *       successfully registered in the <CODE>MBeanServer</CODE>.
1336      *       The value false means that the registration phase has failed.
1337      */
1338     @Override
1339     public void postRegister(Boolean registrationDone) {
1340         if (!registrationDone.booleanValue()) {
1341             synchronized (this) {
1342                 topMBS = bottomMBS = null;
1343             }
1344         }
1345     }
1346 
1347     /**
1348      * Stop the connector.
1349      *
1350      * @exception java.langException This exception should be caught by
1351      *            the <CODE>MBeanServer</CODE> and re-thrown
1352      *            as an <CODE>MBeanRegistrationException</CODE>.
1353      */
1354     @Override
1355     public void preDeregister() throws java.lang.Exception {
1356         synchronized (this) {
1357             topMBS = bottomMBS = null;
1358         }
1359         objectName = null ;
1360         final int cstate = getState();
1361         if ((cstate == ONLINE) || ( cstate == STARTING)) {
1362             stop() ;
1363         }
1364     }
1365 
1366     /**
1367      * Do nothing.
1368      */
1369     @Override
1370     public void postDeregister(){
1371     }
1372 
1373 }