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