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 }